phlex-reactive 0.14.3 → 0.14.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +37 -0
- data/README.md +67 -4
- data/app/javascript/phlex/reactive/core.js +7 -1
- data/app/javascript/phlex/reactive/core.min.js +6 -2
- data/app/javascript/phlex/reactive/core.min.js.map +4 -4
- data/app/javascript/phlex/reactive/features/bindings.js +96 -0
- data/app/javascript/phlex/reactive/features/bindings.min.js +2 -2
- data/app/javascript/phlex/reactive/features/bindings.min.js.map +3 -3
- data/app/javascript/phlex/reactive/features/form.js +12 -5
- data/app/javascript/phlex/reactive/features/form.min.js +2 -2
- data/app/javascript/phlex/reactive/features/form.min.js.map +3 -3
- data/app/javascript/phlex/reactive/features/persist.js +12 -3
- data/app/javascript/phlex/reactive/features/persist.min.js +2 -2
- data/app/javascript/phlex/reactive/features/persist.min.js.map +3 -3
- data/app/javascript/phlex/reactive/features/sortable.js +463 -0
- data/app/javascript/phlex/reactive/features/sortable.min.js +4 -0
- data/app/javascript/phlex/reactive/features/sortable.min.js.map +10 -0
- data/app/javascript/phlex/reactive/reactive_controller.js +2 -0
- data/app/javascript/phlex/reactive/reactive_controller.min.js +6 -2
- data/app/javascript/phlex/reactive/reactive_controller.min.js.map +9 -8
- data/app/javascript/phlex/reactive/runtime.js +117 -10
- data/lib/phlex/reactive/component/editable.rb +78 -0
- data/lib/phlex/reactive/component/helpers.rb +2 -0
- data/lib/phlex/reactive/component/sortable.rb +212 -0
- data/lib/phlex/reactive/engine.rb +4 -4
- data/lib/phlex/reactive/version.rb +1 -1
- metadata +6 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6e80c9d7f2587b324d3bd1658fbe09f93dd45d8fb9ed2dd300f302ac9577c3bc
|
|
4
|
+
data.tar.gz: cfd7d489f859f5c591e8d6a391d9c04b2b4b1906ee9d0067ced14539f8f3d2b3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 79b0d752c36664dc374c69944d127556732864b3a8582ff68ae29d76ca70ad18289d60a375d99671697f91e5609f27e6cc4b7718240dc9fc632ac2425ecae671
|
|
7
|
+
data.tar.gz: 0d19985aa77283b419e815e35691c626f4646356fc04282f5441b4f434b2b15c6a052ade40b1bde8442bead4eb3b3f1900a15cb937dfe9132a9af2b0b16cb57b
|
data/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,43 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
8
8
|
|
|
9
9
|
### Added
|
|
10
10
|
|
|
11
|
+
- **Sortable lists — `reactive_sortable`, `reactive_sortable_source`,
|
|
12
|
+
`reactive_sortable_item` (#350).** Rows drag to reorder a list with a mouse,
|
|
13
|
+
a finger (a 200 ms press) or the keyboard (Space picks up, arrows move,
|
|
14
|
+
Space drops, Escape cancels, each step announced in the list's own
|
|
15
|
+
translatable words), and one declared action receives `item`, `position`,
|
|
16
|
+
`after` and `from` through the normal action pipeline (declare the ones you
|
|
17
|
+
want in `params:`; undeclared ones are dropped). The row moves at once
|
|
18
|
+
and goes back when the action fails or its reply does not render the list.
|
|
19
|
+
Sortables of one `group:` exchange rows (`name:` and static `params:` for a
|
|
20
|
+
kanban), and a `reactive_sortable_source` list's rows are copied in, routed
|
|
21
|
+
to their own action with `from:`. `handle:`, `disabled:`, `busy:` and the
|
|
22
|
+
`data-reactive-sortable-active` / `-dragging` / `-placeholder` styling hooks
|
|
23
|
+
round it out. The client is a new `sortable` feature module, about 3.1 KB
|
|
24
|
+
gzipped: the default client bundles it, and the split client fetches it only
|
|
25
|
+
for a page with a sortable. `core.proceed` now resolves with the reply's
|
|
26
|
+
HTML on success, so a feature can tell an applied reply from a failure. The
|
|
27
|
+
Project board demo drags its cards. Docs: "Sortable lists".
|
|
28
|
+
|
|
29
|
+
- **`reactive_editable(:title)` — a contenteditable element as a first-class,
|
|
30
|
+
plain-text field (#351).** The helper renders `name` (scope-aware),
|
|
31
|
+
`contenteditable="plaintext-only"`, `role="textbox"`, `aria-multiline`,
|
|
32
|
+
`aria-placeholder` and `data-reactive-placeholder` onto your own element. Its
|
|
33
|
+
value is its text with line breaks as `\n` (folded to spaces when
|
|
34
|
+
single-line). While it is focused a `reply.morph` or broadcast morph keeps its
|
|
35
|
+
text and caret and still applies its attributes; unfocused, the server's text
|
|
36
|
+
wins, and `data-reactive-morph-value` opts out. Paste and drop insert plain
|
|
37
|
+
text (a handler, and a downgrade to `contenteditable="true"`, where the engine
|
|
38
|
+
lacks `plaintext-only`); Enter runs `on_enter:` (a `js` chain or `:submit`)
|
|
39
|
+
and is swallowed in a single-line field; Escape reverts to the last
|
|
40
|
+
server-rendered text (`on_escape: :revert`). `reactive_dirty` diffs it against
|
|
41
|
+
that text and `reactive_persist` drafts it with its line breaks. Bare named
|
|
42
|
+
`[contenteditable]` elements keep the `textContent` reading. An editable that a
|
|
43
|
+
stream appends into a root already on the page gets its keys and paste from
|
|
44
|
+
its first focus (the root loads the bindings feature then, and on the split
|
|
45
|
+
client the stream that brings it starts the import). Bundle: default
|
|
46
|
+
+977 B gzip, split core +412 B, `features/bindings` +524 B.
|
|
47
|
+
|
|
11
48
|
- **`on_client(…, detail: { success: true })` — run ops only when the event's
|
|
12
49
|
payload qualifies (#346).** A binding can require top-level `event.detail`
|
|
13
50
|
keys to strictly equal JSON scalars, so `on_client("turbo:submit-end",
|
data/README.md
CHANGED
|
@@ -288,14 +288,15 @@ imports one the first time a root on the page needs it:
|
|
|
288
288
|
|
|
289
289
|
| Module | Gzipped | Default client | Split client |
|
|
290
290
|
|---|---:|---|---|
|
|
291
|
-
| `phlex/reactive/reactive_controller` |
|
|
292
|
-
| `phlex/reactive/core` |
|
|
291
|
+
| `phlex/reactive/reactive_controller` | 28.9 KB | loaded with your controllers | not loaded |
|
|
292
|
+
| `phlex/reactive/core` | 13.5 KB | not loaded (it is inside the file above) | loaded with your controllers |
|
|
293
293
|
| `phlex/reactive/features/persist` (`reactive_persist` drafts) | 3.2 KB | inside the one file | fetched when a root declares `reactive_persist`, or a `persist_state` / `persist_clear` op runs |
|
|
294
294
|
| `phlex/reactive/features/defer` (`reply.defer`, `reactive_lazy`) | 2.8 KB | inside the one file | fetched when a root is a `reactive_lazy` shell, a morph turns one into a shell, or a `reply.defer` arrives |
|
|
295
295
|
| `phlex/reactive/features/form` (dirty tracking, `warn_unsaved`, the paste-trigger gate) | 1.0 KB | inside the one file | fetched when a root tracks dirty fields or holds a `paste_into` trigger |
|
|
296
296
|
| `phlex/reactive/features/effects` (`reactive_effects`, `dismiss_after`) | 1.7 KB | inside the one file | fetched when a root declares an effect, or when the first stream that needs it arrives: one with an effect, or with a dismissing flash |
|
|
297
297
|
| `phlex/reactive/features/bindings` (`reactive_show`, show targets, `reactive_on_complete`, `reactive_filter`, `reactive_tags`, nested rows, the conditional `confirm:`) | 5.4 KB | inside the one file | fetched when a root declares one of them, or holds a trigger with a conditional confirm |
|
|
298
298
|
| `phlex/reactive/features/compute` (`reactive_compute`, `reactive_text`) | 2.1 KB | inside the one file | fetched when a root carries a compute binding |
|
|
299
|
+
| `phlex/reactive/features/sortable` (`reactive_sortable` drag and drop) | 3.1 KB | inside the one file | fetched when a root is or holds a sortable or source list |
|
|
299
300
|
| `phlex/reactive/features/hints` (`optimistic:`, `busy:`) | 1.0 KB | inside the one file | fetched when a root holds a trigger that declares a hint |
|
|
300
301
|
| `phlex/reactive/features/devtools` (the latency simulator, the zero-target warnings, the debug trace) | 1.6 KB | inside the one file | fetched with the core on a page that carries `<meta name="phlex-reactive-env" content="development">` or while a delay is stored for the tab; at connect for a root in debug mode; otherwise the first time a verbose root has something to warn about. In production with none of these, never |
|
|
301
302
|
| `phlex/reactive/early` | 1.2 KB | on every page, if you import it | the same |
|
|
@@ -680,6 +681,7 @@ Use in controllers: `render turbo_stream: Counter.replace(counter)`.
|
|
|
680
681
|
| `on_client(:click, js.toggle("#menu"))` | **Client-only** trigger: applies declared DOM ops with ZERO round trip — no token, no POST, ever. Takes the same `window:`/`once:`/`outside:` modifiers, plus `detail:` (run only when `event.detail` carries the listed values). See [Client-only ops](#client-only-ops-on_client--js--zero-round-trips). |
|
|
681
682
|
| `js` | The immutable op builder behind `on_client`: `show`/`hide`/`toggle` (the `hidden` attribute, with an optional `transition:` and `expanded:`), `add_class`/`remove_class`/`toggle_class`, `set_attr`/`remove_attr`/`toggle_attr` (allowlisted names; `toggle_attr` also flips between two values), `focus`/`focus_first`, `text` (set `textContent` — XSS-safe), `dispatch`, `submit` (requestSubmit the target's own form, optionally through a `submitter:`), `paste_into` (read the clipboard into a field, gesture-gated), `check_group` (tick or untick a checkbox group, then re-sync its bindings), and `persist_state`/`persist_clear` (the `reactive_persist` draft) — chainable. |
|
|
682
683
|
| `reactive_field(:param, **attrs)` | The attribute hash that binds a control to an action param (no magic `name:`) — spread onto any control: `input(**reactive_field(:value, value: @record.name))`, `select(**reactive_field(:status)) { … }`. |
|
|
684
|
+
| `reactive_editable(:name, multiline: false, placeholder: nil, on_enter: nil, on_escape: :revert, spellcheck: true)` | A plain-text **editable element** as a field (issue #351): `h3(**reactive_editable(:title)) { @card.title }`. Its text travels under the scope-aware `name`: with `multiline: true` its line breaks are sent as `\n`; a single-line field (the default) folds them to spaces; a morph keeps the focused element's text and caret (attributes still morph); paste is plain text; Enter runs `on_enter:` (a single-line field swallows it); Escape reverts. `reactive_dirty` and `reactive_persist` treat it like an input. See the [Editable text](https://phlex-reactive.zoolutions.llc/docs/editable-text) page. |
|
|
683
685
|
| `reactive_text(:name, initial)` | Mirror a compute output (or a declared input) into a **text node** — a live preview heading, a character counter, `"Hello, {name}"` — via `textContent` (XSS-safe). The text sibling of `reactive_field`; carries no `name`, so it's never POSTed. See [Client-side computes](#client-side-computes-reactive_compute--reactive_text). |
|
|
684
686
|
| `reactive_show(if:/if_any:/unless:)` | **Value-conditional visibility** (the `x-show`/`data-show` case): spread onto the element to show/hide — it toggles `hidden` from the fields' **current values**, client-only, zero round trip. One conditions language: a **Hash is an AND**, an **Array is membership**, a **Range is a threshold**, `if_any:` is OR-of-AND, `unless:` negates. `reactive_values` computes first paint; `disable:` disables a hidden section's controls. See [Value-conditional visibility](#value-conditional-visibility-reactive_show). |
|
|
685
687
|
| `reactive_enable(if:/if_any:/unless:)` | The sibling of `reactive_show` that flips the element's **own `disabled`** (a button, a fieldset) instead of `hidden` — same conditions, same `reactive_values` first paint. `button(**reactive_enable(if: { "ids[]" => { checked: 1.. } }))`. See [Bulk selection](#bulk-selection-reactive_select_all-reactive_count-reactive_enable). |
|
|
@@ -1139,8 +1141,9 @@ string is legacy and ignored), and `turbo:before-visit` **does not fire on
|
|
|
1139
1141
|
restoration visits** (Back/Forward) — a documented Turbo gap, not a
|
|
1140
1142
|
phlex-reactive one.
|
|
1141
1143
|
|
|
1142
|
-
**Out of scope (v1):** rich-text
|
|
1143
|
-
baseline and are **not** tracked
|
|
1144
|
+
**Out of scope (v1):** rich-text editors (Lexxy, Trix) and bare
|
|
1145
|
+
`contenteditable` elements have no `default*` baseline and are **not** tracked;
|
|
1146
|
+
a `reactive_editable` is (against the text of its last server render, #351). Combining `reactive_field` with your own
|
|
1144
1147
|
`data:`/`on(...)` still needs `mix(...)` (a bare merge clobbers the `data-action`
|
|
1145
1148
|
the descriptor rides on — the same rule as everywhere else).
|
|
1146
1149
|
|
|
@@ -1635,6 +1638,66 @@ button(id: "bulk-archive", **reactive_group_target_attrs("ids[]", :enable, 1..))
|
|
|
1635
1638
|
`(group, :enable, condition)` is `{ disabled: }` (or `{}` when
|
|
1636
1639
|
`reactive_values` does not cover the group, and the client seeds it).
|
|
1637
1640
|
|
|
1641
|
+
### Sortable lists (`reactive_sortable`)
|
|
1642
|
+
|
|
1643
|
+
Drag rows to reorder a list — with a mouse, a finger or the keyboard — and one
|
|
1644
|
+
declared action receives the row and where it landed. No Stimulus controller,
|
|
1645
|
+
no endpoint of your own: the drop goes through the action pipeline (signed
|
|
1646
|
+
token, request queue, `busy:`, the reply).
|
|
1647
|
+
|
|
1648
|
+
```ruby
|
|
1649
|
+
class TaskList < Phlex::HTML
|
|
1650
|
+
include Phlex::Reactive::Component
|
|
1651
|
+
|
|
1652
|
+
reactive_record :list
|
|
1653
|
+
action :move, params: { item: :integer, position: :integer }
|
|
1654
|
+
|
|
1655
|
+
def move(item:, position:)
|
|
1656
|
+
authorize! @list, :update?
|
|
1657
|
+
# insert_at is acts_as_list's; any reorder that honours `position` works.
|
|
1658
|
+
@list.tasks.find(item).insert_at(position + 1)
|
|
1659
|
+
reply.morph
|
|
1660
|
+
end
|
|
1661
|
+
|
|
1662
|
+
def view_template
|
|
1663
|
+
ul(**mix(reactive_root, reactive_sortable(:move, handle: "[data-drag-handle]"))) do
|
|
1664
|
+
@list.tasks.each do |task|
|
|
1665
|
+
li(id: dom_id(task), **reactive_sortable_item(task.id)) do
|
|
1666
|
+
span(data: { drag_handle: true }) { "⠿" }
|
|
1667
|
+
plain task.title
|
|
1668
|
+
end
|
|
1669
|
+
end
|
|
1670
|
+
end
|
|
1671
|
+
end
|
|
1672
|
+
end
|
|
1673
|
+
```
|
|
1674
|
+
|
|
1675
|
+
On drop the action gets `item` (the row's key), `position` (its 0-based index
|
|
1676
|
+
after the drop), `after` (the key of the row it now follows, `nil` when first)
|
|
1677
|
+
and `from` (`"self"` for a reorder) — declare the ones you want. The row moves
|
|
1678
|
+
at once and goes back if the action fails or its reply does not render the
|
|
1679
|
+
list; a reply that morphs the list puts the rows in the server's order.
|
|
1680
|
+
|
|
1681
|
+
- **Drag in from another list**: `reactive_sortable_source(group:, name:)` rows
|
|
1682
|
+
are copied into any sortable of the same group (the source needs no token —
|
|
1683
|
+
it works on a `ClientBindings` root). Route a drag-in to its own action with
|
|
1684
|
+
`from: { templates: :insert }`. Source rows are pointer-only: pass
|
|
1685
|
+
`tabindex: -1` (via `mix`) so they stay out of the tab order.
|
|
1686
|
+
- **Between lists** (a kanban): sortables of one `group:` exchange rows; `name:`
|
|
1687
|
+
is what a row dragged out reports as `from`, and static `params:` (e.g.
|
|
1688
|
+
`{ lane: "doing" }`) tell the action which list it landed in.
|
|
1689
|
+
- **Keyboard**: a row is focusable; Space/Enter picks it up, the arrows move it
|
|
1690
|
+
(← → into the previous/next list of the group), Space/Enter drops it,
|
|
1691
|
+
Escape puts it back. Each step is announced through a live region; translate
|
|
1692
|
+
the words with `messages:` (`{position}`, `{count}`).
|
|
1693
|
+
- **Styling**: `data-reactive-sortable-active` (the list), `-dragging` (the
|
|
1694
|
+
row) and `-placeholder` (the gap) are yours to style.
|
|
1695
|
+
- **Guards**: `disabled: true` on the list or a row, and a request in flight on
|
|
1696
|
+
the root, refuse a drag. A drag starts after a 4 px move, or a 200 ms touch
|
|
1697
|
+
press, so clicks, taps and scrolling still work.
|
|
1698
|
+
|
|
1699
|
+
Full guide: [Sortable lists](https://phlex-reactive.zoolutions.llc/docs/sortable-lists).
|
|
1700
|
+
|
|
1638
1701
|
### Client-only drafts (`reactive_persist`)
|
|
1639
1702
|
|
|
1640
1703
|
"Don't make me start over": a public application form, a multi-step wizard,
|
|
@@ -26,6 +26,7 @@ registerReactiveFeatureLoader("defer", () => import("phlex/reactive/features/def
|
|
|
26
26
|
registerReactiveFeatureLoader("form", () => import("phlex/reactive/features/form"))
|
|
27
27
|
registerReactiveFeatureLoader("bindings", () => import("phlex/reactive/features/bindings"))
|
|
28
28
|
registerReactiveFeatureLoader("compute", () => import("phlex/reactive/features/compute"), waitingForEdit)
|
|
29
|
+
registerReactiveFeatureLoader("sortable", () => import("phlex/reactive/features/sortable"))
|
|
29
30
|
registerReactiveFeatureLoader("effects", () => import("phlex/reactive/features/effects"))
|
|
30
31
|
registerReactiveFeatureLoader("hints", () => import("phlex/reactive/features/hints"))
|
|
31
32
|
registerReactiveFeatureLoader("devtools", () => import("phlex/reactive/features/devtools"))
|
|
@@ -144,7 +145,8 @@ onReactiveStreamWithoutEffects((event, loadedEffects) => {
|
|
|
144
145
|
})
|
|
145
146
|
|
|
146
147
|
// The reactive roots a stream is about to put on the page: the roots in its
|
|
147
|
-
// template (the template's own root element included)
|
|
148
|
+
// template (the template's own root element included), and the content
|
|
149
|
+
// itself when it brings an editable.
|
|
148
150
|
function incomingRoots(streamEl) {
|
|
149
151
|
const content = streamEl?.querySelector?.("template")?.content
|
|
150
152
|
if (!content) return []
|
|
@@ -152,6 +154,10 @@ function incomingRoots(streamEl) {
|
|
|
152
154
|
for (const child of content.children ?? []) {
|
|
153
155
|
if (child.matches?.('[data-controller~="reactive"]') && !roots.includes(child)) roots.unshift(child)
|
|
154
156
|
}
|
|
157
|
+
// An editable appended into a root already on the page (issue #351): that
|
|
158
|
+
// root loads the bindings module when the editable is first focused, so
|
|
159
|
+
// start the import now — the module is then here before anything is typed.
|
|
160
|
+
if (content.querySelector?.("[data-reactive-editable]")) roots.push(content)
|
|
155
161
|
return roots
|
|
156
162
|
}
|
|
157
163
|
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import{Controller as Ae}from"@hotwired/stimulus";import{confirmResolver as U}from"phlex/reactive/confirm";function Ee(){let e=window.Turbo?.StreamActions;if(!e||e["reactive:visit"])return;e["reactive:visit"]=function(){let t=this.getAttribute("data-url");if(t)window.Turbo.visit(t,{action:"advance"})}}function Fe(){let e=window.Turbo?.StreamActions;if(!e||e["reactive:token"])return;e["reactive:token"]=function(){let t=this.getAttribute("data-reactive-token-value"),r=this.getAttribute("target");if(!t||!r)return;let n=document.getElementById(r);if(n)n.setAttribute("data-reactive-token-value",t)}}function ke(){let e=window.Turbo?.StreamActions;if(!e||e["reactive:js"])return;e["reactive:js"]=function(){let t=j(this.getAttribute("data-reactive-ops"));if(!t.length)return;let r=this.getAttribute("target"),n=this.getAttribute("data-reactive-verbose")==="true",i=r?document.getElementById(r):null;if(r&&!i){if(n)b("devtools",(o)=>o.missingRoot(r));return}q(t,(o)=>et(o,i),n?(o,a)=>b("devtools",(s)=>s.diagnoseStream(o,a,i)):void 0)}}function Le(){let e=window.Turbo?.StreamActions;if(!e||e["reactive:defer"])return;e["reactive:defer"]=function(){b("defer",(t)=>t.streamAction(this))}}var J=!1,D=null;function Re(){if(J)return;if(typeof document>"u"||typeof document.addEventListener!=="function")return;J=!0,document.addEventListener("turbo:before-stream-render",(e)=>{if(D)D(e,m.get("effects"));else m.get("effects")?.wrap(e)})}function oe(e){D=e}var K=!1;function z(e){return e===document.activeElement&&(e.localName==="input"||e.localName==="textarea")&&!e.hasAttribute("data-reactive-morph-value")&&!!e.closest('[data-controller~="reactive"]')}function xe(){if(K)return;if(typeof document>"u"||typeof document.addEventListener!=="function")return;K=!0,document.addEventListener("turbo:before-morph-element",(e)=>{let t=e.target,r=e.detail?.newElement;if(!r||e.defaultPrevented||t.localName!=="input"||!z(t))return;let n=r.getAttribute("value");if(n==null)t.removeAttribute("value");else if(t.getAttribute("value")!==n)t.setAttribute("value",n)}),document.addEventListener("turbo:before-morph-attribute",(e)=>{if(e.detail?.attributeName==="value"&&z(e.target))e.preventDefault()})}var Pe="__materialize",H=!1;function Ie(){if(H)return;if(typeof window>"u"||typeof document>"u")return;if(typeof window.addEventListener!=="function")return;H=!0;let e=()=>{let t=document.documentElement;if(typeof t?.toggleAttribute!=="function")return;t.toggleAttribute("data-reactive-offline",globalThis.navigator?.onLine===!1)};e(),window.addEventListener("online",e),window.addEventListener("offline",e)}var Ne="phlex-reactive:latency";function ae(){return typeof sessionStorage<"u"&&Boolean(sessionStorage.getItem(Ne))}function B(){if(typeof document>"u")return;if(!m.has("devtools")&&!v.get("devtools")[1])return;let e=document.querySelector?.('meta[name="phlex-reactive-env"]')?.content==="development";if(e||ae())b("devtools",(t)=>e&&t.attach())}var ce="data-reactive-active",_=0;function ue(){if(_++,_===1)fe("reactive:busy")}function le(){if(_===0)return;if(_--,_===0)fe("reactive:idle")}function Ft(){return _}function kt(){_=0,(typeof document<"u"?document.documentElement:null)?.removeAttribute?.(ce)}function fe(e){if(typeof document>"u")return;let t=document.documentElement;if(typeof t?.toggleAttribute==="function")t.toggleAttribute(ce,_>0);if(typeof document.dispatchEvent==="function"&&typeof CustomEvent==="function")document.dispatchEvent(new CustomEvent(e,{detail:{count:_}}))}var de="data-reactive-requests";function he(e){if(typeof document>"u")return;let t=document.documentElement;if(typeof t?.setAttribute!=="function"||!Oe(t))return;let r=Me(t);r[e]=(Number(r[e])||0)+1,t.setAttribute(de,JSON.stringify(r))}function Oe(e){return e.hasAttribute?.("data-reactive-verbose")||!!document.querySelector?.('[data-reactive-verbose="true"]')}function Me(e){try{let t=JSON.parse(e.getAttribute(de));if(t&&typeof t==="object")return{action:0,defer:0,...t}}catch{}return{action:0,defer:0}}function G(){Ee(),Fe(),ke(),Le(),Re(),xe(),Ie(),B()}function Ce(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}var me=Symbol.for("phlex-reactive.core");if(globalThis[me])console.error("[phlex-reactive] the client was loaded twice. Import EITHER phlex/reactive/reactive_controller (the default, "+"everything in one file) OR phlex/reactive/core (features on demand), not both — each carries its own state.");globalThis[me]=!0;var pe=!1;function De(){if(pe)return;if(typeof document>"u")return;let e=document.querySelectorAll('[data-controller~="reactive"]');if(!e||e.length===0)return;console.warn("[phlex-reactive] found "+e.length+' element(s) with data-controller="reactive" '+"but the reactive controller never connected. It is loaded but not registered — "+'add `application.register("reactive", ReactiveController)` (importmap) or import it into app/javascript/controllers/ for lazyLoadControllersFrom apps. See the README.')}if(typeof window<"u"&&typeof document<"u"){let e=()=>setTimeout(De,0);if(document.readyState==="loading")document.addEventListener("DOMContentLoaded",e,{once:!0});else e()}var qe=new Set(["href","src","srcdoc","action","formaction","xlink:href","style"]);function $e(e){let t=String(e).toLowerCase();return t.startsWith("on")||qe.has(t)}var I=new WeakMap;function Be(e,t,r){let[n,i,o]=t;I.get(e)?.(),e.classList.add(n,i),r();let a=!1,s=requestAnimationFrame(()=>{if(a)return;e.classList.remove(i),e.classList.add(o)}),c=(h)=>{if(a||h&&h.target!==e)return;a=!0,I.delete(e),clearTimeout(l),globalThis.cancelAnimationFrame?.(s),e.removeEventListener("animationend",c),e.removeEventListener("transitionend",c),e.classList.remove(n,i,o)};I.set(e,c),e.addEventListener("animationend",c),e.addEventListener("transitionend",c);let l=setTimeout(c,je(e))}function je(e){let t=0;try{let r=getComputedStyle(e);for(let n of["transition","animation"]){let[i,o]=["Duration","Delay"].map((s)=>String(r[n+s]).split(",").map((c)=>parseFloat(c)*(/ms/.test(c)?1:1000)||0)),a=String(r[n+"IterationCount"]).split(",").map((s)=>/inf/.test(s)?1/0:parseFloat(s)>=0?parseFloat(s):1);for(let s=0;s<i.length||s<o.length||s<a.length;s++){let c=i[s%i.length]*a[s%a.length]||0;t=Math.max(t,c+o[s%o.length])}}}catch{}return Math.min(Math.max(t+50,350),5000)}var We=":is(lexxy-editor, trix-editor, [contenteditable=''], [contenteditable=true], [contenteditable=plaintext-only])",Ue=new Set(["lexxy-editor","trix-editor"]);function Je(e){return Ue.has(e.localName)?typeof e.value==="string":!0}var V=Object.freeze({show:(e,t,r)=>N(e,!1,t,r),hide:(e,t,r)=>N(e,!0,t,r),toggle:(e,t,r)=>N(e,!e.hidden,t,r),add_class:(e,t)=>e.classList.add(...t.classes??[]),remove_class:(e,t)=>e.classList.remove(...t.classes??[]),toggle_class:(e,t)=>(t.classes??[]).forEach((r)=>e.classList.toggle(r)),set_attr:(e,t)=>{if(O(t.name))e.setAttribute(t.name,t.value??"")},remove_attr:(e,t)=>{if(O(t.name))e.removeAttribute(t.name)},toggle_attr:(e,t)=>{if(!O(t.name))return;if(Array.isArray(t.values)){let[r,n]=t.values;e.setAttribute(t.name,e.getAttribute(t.name)===r?n:r)}else if(e.hasAttribute(t.name))e.removeAttribute(t.name);else e.setAttribute(t.name,"")},focus:(e)=>e.focus?.(),focus_first:(e)=>Ve(e)?.focus?.(),text:(e,t)=>{let r=String(t.value??"");if(e.textContent!==r)e.textContent=r},check_group:(e,t)=>{let r=t.global?document.querySelectorAll('[data-controller~="reactive"]'):[e];for(let n of r)b("bindings",(i)=>i.checkGroup(n,t.group,t.checked))},dispatch:(e,t)=>{e.dispatchEvent(new CustomEvent(t.name,{bubbles:!0,composed:!0,detail:t.detail??{}}))},submit:(e,t,r)=>{let n=Ke(e);if(!n)return;let i=null;if(t?.submitter!=null&&typeof r==="function"){if(i=r({...t,to:t.submitter})[0]??null,!(i?.form===n&&(i.type==="submit"||i.type==="image")))console.warn(`[phlex-reactive] submitter ${t.submitter} is not a submit control of the form — ignored`),i=null}i?n.requestSubmit?.(i):n.requestSubmit?.()},paste_into:(e)=>ze(e),persist_state:(e,t)=>b("persist",(r)=>r.writeState(e,t.state)),persist_clear:(e)=>b("persist",(t)=>t.clearRoot(e))});function Ke(e){if(e?.tagName==="FORM")return e;return e?.form??e?.closest?.("form")??null}function ze(e){let t=globalThis.navigator?.clipboard;if(typeof t?.readText!=="function")return;t.readText().then((r)=>{if(!r)return;if(e.value=r,typeof e.dispatchEvent==="function")e.dispatchEvent(new Event("input",{bubbles:!0}));e.focus?.()}).catch(()=>{})}function He(e,t,r){if(r?.transition)Be(e,r.transition,()=>e.hidden=t);else e.hidden=t}function N(e,t,r,n){if(He(e,t,r),r?.expanded==null||typeof n!=="function")return;for(let i of n({...r,to:r.expanded}))i.setAttribute?.("aria-expanded",String(!t))}function O(e){if(!$e(e))return!0;return console.warn(`[phlex-reactive] refused client attr op on ${JSON.stringify(e)} — skipped`),!1}var Ge='a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';function Ve(e){return e.querySelectorAll?.(Ge)?.[0]??null}function j(e){if(Array.isArray(e))return e;if(typeof e!=="string")return[];try{let t=JSON.parse(e);return Array.isArray(t)?t:[]}catch{return[]}}var Y=Object.freeze({enter:"Enter",tab:"Tab",esc:"Escape",space:" ",up:"ArrowUp",down:"ArrowDown",left:"ArrowLeft",right:"ArrowRight",home:"Home",end:"End",page_up:"PageUp",page_down:"PageDown"}),X=Object.freeze(["meta","ctrl","alt","shift"]);function W(e,t,r){let n=e.split("+");for(let a of X)if(n.includes(a)!==Boolean(t[`${a}Key`]))return!1;let i=n.find((a)=>!X.includes(a));if(i===void 0||typeof t.key!=="string")return!0;let o=r&&Object.hasOwn(r,i)?r[i]:Object.hasOwn(Y,i)?Y[i]:/^[a-z0-9]$/.test(i)?i:null;return typeof o==="string"&&o.toLowerCase()===t.key.toLowerCase()}function Ye(e){if(Array.isArray(e))return[{ops:e,legacy:!0}];if(e&&typeof e==="object")return[e];if(typeof e!=="string")return[];let t=e.trim();if(t.startsWith("["))return[{ops:j(t),legacy:!0}];let r=[];for(let n of t.split(" ")){if(n==="")continue;let i=null;try{i=JSON.parse(n)}catch{i=null}if(i&&typeof i==="object"&&!Array.isArray(i))r.push(i);else console.warn(`[phlex-reactive] malformed on_client binding record ${JSON.stringify(n)} — skipped`)}return r}function Z(e,t,r){if(e.legacy)return!0;let n=e.on;if(typeof n!=="string"||n==="")return!1;let i=n.indexOf(".");if(t.type!==(i<0?n:n.slice(0,i)))return!1;if(Boolean(e.window)!==_e(t.currentTarget))return!1;return i<0||r===null||W(n.slice(i+1),t,r)}function Xe(e,t){let r=e.detail;if(r===void 0)return!0;if(r===null||typeof r!=="object"||Array.isArray(r))return!1;let n=t.detail;if(n===null||typeof n!=="object")return!1;return Object.keys(r).every((i)=>n[i]===r[i])}var Q=new WeakMap;function Ze(e,t,r){if(!r.once)return!1;let n=t.currentTarget??e.element,i=Q.get(e);if(!i)i=new WeakMap,Q.set(e,i);let o=i.get(n);if(!o)o=new Set,i.set(n,o);let a=t.params?.ops,c=(n===globalThis.window?`${typeof a==="string"?a:JSON.stringify(a??null)}|`:"")+JSON.stringify(r);if(o.has(c))return!0;return o.add(c),!1}var ee=new WeakMap;function Qe(e,t){if(e===null||typeof e!=="object")return!1;let r=e.params?.ops,n=typeof r==="string"?r:JSON.stringify(r??null),i=ee.get(e);if(!i)i=new WeakMap,ee.set(e,i);let o=i.get(t);if(!o)o=new Map,i.set(t,o);let a=o.get(e.currentTarget);if(!a)a=new Set,o.set(e.currentTarget,a);if(a.has(n))return!0;return a.add(n),!1}function q(e,t,r){for(let n of e){if(!Array.isArray(n))continue;let[i,o={}]=n;if(!Object.hasOwn(V,i)){console.warn(`[phlex-reactive] unknown client op ${JSON.stringify(i)} — skipped`);continue}let a=t(o);if(a.length===0&&r)r(i,o);for(let s of a)V[i](s,o,t)}}function et(e,t){let r=e.to;if(t){if(r==="@root")return[t];if(typeof r!=="string"||r==="")return[];if(e.global)return[...document.querySelectorAll(r)];return[...t.querySelectorAll(r)]}if(typeof r!=="string"||r===""||r==="@root")return[];return[...document.querySelectorAll(r)]}function tt(e){let t=e.getAttribute?.("data-reactive-persist");return Boolean(t)&&t!=="off"}function ge(e){return["defer-token","defer-src","lazy-on","lazy-visible"].some((t)=>e.getAttribute?.(`data-reactive-${t}`)!=null)}function rt(e){return(e.getAttribute?.("data-action")??"").includes("reactive#trackDirty")||e.getAttribute?.("data-reactive-clipboard")!=null||e.querySelector?.('[data-action*="reactive#trackDirty"],[data-reactive-clipboard]')!=null}function nt(e){return["show-targets","group-targets","on-complete","filter-input","tags-field"].some((t)=>e.getAttribute?.(`data-reactive-${t}`)!=null)||(e.querySelectorAll?.("[data-reactive-show-field], [data-reactive-show]")??[]).length>0||e.querySelector?.("[data-reactive-nested-json], [data-reactive-nested-list], [data-reactive-confirm-when-param], [data-reactive-enable], [data-reactive-select-all], [data-reactive-count]")!=null}function it(e){return e.getAttribute?.("data-reactive-compute-inputs-param")!=null}var st=[["persist",[tt,null,null,!0]],["defer",[ge,null]],["form",[rt,null]],["bindings",[nt,null,null,!0]],["compute",[it,null,null,!0]],["effects",[(e)=>["enter","update","exit"].some((t)=>e.getAttribute?.(`data-reactive-effect-${t}`)!=null),null]],["hints",[(e)=>e.querySelector?.("[data-reactive-optimistic-param], [data-reactive-busy-param], [data-reactive-loading-param]")!=null,null]],["devtools",[(e)=>e.getAttribute?.("data-reactive-debug")==="true",null]]],v=new Map(st),te=new Map,m=new Map,ot=new Map;function w(e,t,r){ye.set(e,[t,r]),at(e)}var ye=new Map;function at(e){let t=v.get(e),[r,n]=ye.get(e);t[1]=r,t[2]=n}function Lt(e,t){t.install?.(F),m.set(e,t),ot.set(e,t)}function be(e){let t=[];for(let[r,[n]]of v){if(!n||m.has(r))continue;try{if(e.some((i)=>n(i)))t.push(r)}catch{}}return t}function ve(e){return E(e).catch((t)=>R(e,"load",t))}function E(e){let t=te.get(e);if(!t)t=(v.get(e)[1]??(()=>Promise.reject(Error("this entry has no way to import it"))))().then((n)=>(n.install?.(F),m.set(e,n),n)),te.set(e,t);return t}var re=new Set;function R(e,t,r){let n=e+t;if(re.has(n))return;re.add(n),console.error(`[phlex-reactive] the "${e}" feature module (phlex/reactive/features/${e}) is unavailable: ${t}. If you bundle or vendor the client, alias or pin phlex/reactive/features/* (README: esbuild / webpack / bun).`,r)}function b(e,t){let r=m.get(e);if(r)return t(r);E(e).then(t,(n)=>R(e,"load",n))}var ct=1e4;function M(){let e=Number(globalThis.document?.head?.querySelector?.('meta[name="phlex-reactive-feature-timeout"]')?.content);return Number.isFinite(e)&&e>0?e:ct}function Rt(){return[...v.keys()]}var ne=Promise.resolve();function ie(e,t,r){let n=F.waiting.get(e);if(n&&n[t]===r)delete n[t]}var F={enter:ue,exit:le,count:he,waiting:new WeakMap};var k=Symbol.for("phlex-reactive.early"),ut=1e4,lt=1500,_e=(e)=>e!=null&&(e===globalThis.window||e.window===e);function C(){return globalThis[k]??={queue:[],connected:new WeakSet},globalThis[k]}function ft(){let e=Number(globalThis.document?.querySelector?.('meta[name="phlex-reactive-early-ttl"]')?.content);return Number.isFinite(e)&&e>0?e:ut}function dt(e){let t={};for(let{name:r,value:n}of Array.from(e.attributes??[])){let i=/^data-reactive-(.+)-param$/i.exec(r);if(!i)continue;let o;try{o=JSON.parse(n)}catch{o=n}t[i[1].replace(/[-_]([a-z0-9])/g,(a,s)=>s.toUpperCase())]=o}return t}function ht(e,t,r){return{[k]:!0,type:e.type,detail:e.detail,target:e.target,currentTarget:r?t.ownerDocument.defaultView:t,params:dt(t),key:e.key,code:e.code,metaKey:e.metaKey,ctrlKey:e.ctrlKey,altKey:e.altKey,shiftKey:e.shiftKey,button:e.button,submitter:e.submitter,bubbles:e.bubbles,timeStamp:e.timeStamp,defaultPrevented:e.defaultPrevented,preventDefault(){},stopPropagation(){},stopImmediatePropagation(){}}}var x=new WeakMap;function mt(e,{token:t,type:r,method:n,filter:i,win:o},a){let s=x.get(e);if(!s)x.set(e,s=new Map);let c=o?`${t} ${a}`:t;if(s.has(c))return!1;return s.set(c,{type:r,method:n,filter:i,win:o,params:a,armed:!0}),!0}function se(e,t,r,n){if(e?.[k])return!1;let i=_e(e?.currentTarget),o=i?n:e?.currentTarget,a=gt(e,o,i?`@${t}`:t),s=x.get(o),c=i&&JSON.stringify(e.params);for(let l of s?.values()??[]){if(!l.armed||l.method!==t||l.type!==e.type||Boolean(l.win)!==i)continue;if(i&&l.params!==c)continue;if(l.filter&&!W(l.filter,e,r))continue;return l.armed=!1,!0}return a}var L=new WeakMap;function pt(e,t,r){let n=L.get(e);if(!n)L.set(e,n=new Map),setTimeout(()=>L.delete(e));let i=n.get(t)??{};n.set(t,i),i[r]=(i[r]??0)+1}function gt(e,t,r){let n=e&&L.get(e)?.get(t);if(!n?.[r])return!1;return n[r]-=1,!0}class we extends Ae{static values={token:String};#v;#_=new Map;#u=new Map;#Q;#L;#W;#R=0;#l=new Map;#x=new WeakMap;#w;featuresReady=ne;#t=new Map;#P=new Set;#f=!1;#r=null;#d=new Map;#S=new Map;#h=new Map;#m=0;#U;#Ne=0;#Oe=0;connect(){if(pe=!0,C().connected.add(this.element),this.element.id==="")console.warn("[phlex-reactive] a reactive root has no id; its next-action token can't self-match "+"and may fall back to the first token in the response → a silent HTTP 403 on the NEXT action. "+"Put id: on the SAME element as reactive_attrs — use div(**reactive_root) (emits id + token together), "+"or div(id:, **reactive_attrs). The id: must NOT be on a child. See the README.");if(this.element.getAttribute?.("data-reactive-token-value")!=null||ge(this.element))this.#w=(e)=>{if(e.target!==this.element)return;this.#J(!0)},this.element.addEventListener?.("turbo:morph-element",this.#w);this.#m++,this.featuresReady=ne,this.#J(),this.#ie()}#Me(e){}#Ce(e,t,r,n){}#De(){}#qe(e,t){}#J(e){{let t=this.#m,r=[];for(let[o,[a]]of v){if(!a||this.#P.has(o))continue;let s=m.get(o);if(s&&!s.connect)continue;try{if(a(this.element))r.push(o)}catch(c){this.#p(o,"detect",c)}}if(r.length===0)return;for(let o of r)this.#P.add(o);if(!this.#f&&r.every((o)=>m.has(o))){for(let o of r)this.#I(t,o,m.get(o),e);return}for(let o of r){let a=this.#ee(t,o),s=[];for(let l of v.keys()){if(l===o)break;if(this.#h.has(l))s.push(this.#h.get(l))}let c=Promise.all(s).then(()=>a).then((l)=>l&&this.#I(t,o,l,e));if(this.#h.set(o,c),v.get(o)[3])this.#r=c}let n=this.#r;n?.then(()=>{if(this.#r===n)this.#r=null}),this.#f=!0;let i=Promise.all(this.#h.values()).then(()=>{if(this.featuresReady===i)this.#f=!1});this.featuresReady=i}}#ee(e,t){{let r=()=>e===this.#m;return this.#te(t),new Promise((n)=>{let i=!1,o=setTimeout(()=>{if(this.#d.delete(o),i=!0,r())this.#p(t,"timeout",Error(`not loaded after ${M()} ms`));n(null)},M());this.#d.set(o,n);let a=()=>{clearTimeout(o),this.#d.delete(o)};E(t).then((s)=>{if(a(),i)queueMicrotask(()=>this.#I(e,t,s));else n(s)},(s)=>{if(a(),r())this.#p(t,"load",s);n(null)})})}}#te(e){{let t=v.get(e)[2];if(!t)return;let r={};try{this.#S.set(e,[t(this.element,r),r]),F.waiting.set(this.element,{...F.waiting.get(this.element),[e]:r})}catch(n){this.#p(e,"detect",n)}}}#K(e,t){{let[r,n]=this.#S.get(e)??[];if(this.#S.delete(e),!t)ie(this.element,e,n);try{r?.()}catch(i){console.error(`[phlex-reactive] the "${e}" feature module failed to stop waiting`,i)}return n}}#I(e,t,r,n){{if(e!==this.#m)return;let i=this.#K(t);try{r.connect?.(this,this.#e(),n,i),this.#t.set(t,r)}catch(o){this.#p(t,"connect",o)}}}#e(){return this.#U??={emit:(e,t,r)=>this.#i(e,t,r),reseed:()=>this.#re(),proceed:(e,t,r)=>this.#O(e,t,r),forgetToken:()=>{this.#v=void 0},owns:(e)=>this.#F(e),ownership:()=>this.#D(),opTargets:(e)=>this.#j(e),diagnose:(e,t)=>this.#B(e,t),applyOps:(e,t)=>q(e,(r)=>this.#j(t!=null&&r.to==null?{...r,to:t}:r),(r,n)=>this.#B(`client op "${r}"`,n)),listnavOptions:(e)=>this.#y(e),collectFields:()=>this.#Y(),confirm:(e,t)=>Promise.resolve().then(()=>U(e,t)).catch(()=>!1)},this.#U}#re(){this.#t.get("form")?.scan(this,this.#e()),this.#t.get("bindings")?.reseed(this),this.#t.get("compute")?.seed(this)}#p(e,t,r){if(R(e,t,r),this.#n("feature"),t==="load"&&this.element.getAttribute?.("data-reactive-defer-pending"))this.element.removeAttribute("data-reactive-defer-pending"),this.element.removeAttribute("aria-busy");this.#i("reactive:error",{kind:"feature",feature:e,phase:t,error:r})}#ne(){{this.#m++;for(let[t,r]of this.#d)clearTimeout(t),r(null);this.#d.clear();for(let t of[...this.#S.keys()]){let r=this.#K(t,!0),n=this.element;b(t,(i)=>{i.abandon?.(n,r),ie(n,t,r)})}this.#P.clear(),this.#h.clear(),this.#f=!1,this.#r=null;let e=[...this.#t];this.#t.clear();for(let[t,r]of e)try{r.disconnect?.(this,this.#e())}catch(n){console.error(`[phlex-reactive] the "${t}" feature module failed to disconnect`,n)}}}#ie(){if(this.element.setAttribute?.("data-reactive-connected",""),this.element.isConnected)this.element.dispatchEvent?.(new CustomEvent("reactive:connect",{bubbles:!0,composed:!0,detail:{id:this.element.id}}));this.#se()}#N=new Set;#se(){let{queue:e}=C();if(e.length===0)return;let t=[];for(let i=e.length-1;i>=0;i--){let{root:o}=e[i];if(o===this.element||!o.isConnected)t.unshift(...e.splice(i,1))}let r=ft(),n=new Map;for(let i of t){let o=n.get(i.event)??[new Set,new Set];n.set(i.event,o);let a=o[+!!i.win];if(a.has(i.el))continue;a.add(i.el);let s=i.win?Math.min(lt,r):r,c=i.root!==this.element?"its root left the page before a controller connected":performance.now()-i.at>s?`it is older than the ${s} ms ${i.win?"window-trigger":"early-event"} TTL`:i.el.isConnected&&this.element.contains(i.el)?null:"its element left the root before the controller connected";if(c){if(this.#$())console.warn(`[phlex-reactive] dropped an early "${i.event.type}" trigger: ${c}`);continue}this.#oe(i)}}#oe({event:e,el:t,descs:r,win:n}){let i=ht(e,t,n),o=this.application?.schema?.keyMappings,a=(t.getAttribute("data-action")??"").split(/\s+/),s=n?this.element:t;for(let c of r){if(!a.includes(c.token))continue;if(c.filter&&!W(c.filter,e,o))continue;if(/#\w+.*:once\b/.test(c.token)){if(!mt(s,c,n&&JSON.stringify(i.params)))continue;this.#N.add(s)}if(pt(e,s,n?`@${c.method}`:c.method),c.method==="runOps")this.runOps(i);else this.dispatch(i)}}disconnect(){if(this.#ne(),this.#fe(),this.#he(),this.#w)this.element.removeEventListener?.("turbo:morph-element",this.#w);C().connected.delete(this.element),this.element.removeAttribute?.("data-reactive-connected");for(let e of this.#N)x.delete(e);this.#N.clear()}dispatch(e){if(se(e,"dispatch",this.application?.schema?.keyMappings,this.element))return;let{action:t,params:r,debounce:n,throttle:i,confirm:o,confirmWhen:a,outside:s,window:c,optimistic:l}=e.params;if(!t)return;let h=e.params.busy??e.params.loading;if(s&&this.element.contains(e.target))return;let d=e.currentTarget??e.target;if(!c&&!this.#Ee(l,d))e.preventDefault();if(t===Pe){let y=()=>{{let T=this.#t.get("defer");if(T)return T.materialize(this)}if(this.tokenValue)return this.#O(d,t,"{}")};return this.#f?this.featuresReady.then(y):y()}let f=this.#z(o,a),g=()=>this.#O(d,t,r,n,i,l,h);if(!f)return g();if(f instanceof Promise)return f.then((y)=>y?this.#T(y,d,g):g());this.#T(f,d,g)}#T(e,t,r){return Promise.resolve().then(()=>U(e,{el:t})).catch(()=>!1).then((n)=>{if(n)r()})}#z(e,t){if(e)return e;if(!t)return null;{let r=m.get("bindings");if(r)return r.confirmMessage(this,this.#e(),t);return this.#H("bindings").then((n)=>n&&n.confirmMessage(this,this.#e(),t))}}#H(e){{let t;return Promise.race([E(e).catch((r)=>(R(e,"load",r),null)),new Promise((r)=>t=setTimeout(()=>r(null),M()))]).finally(()=>clearTimeout(t))}}runOps(e){if(se(e,"runOps",this.application?.schema?.keyMappings,this.element))return;if(Qe(e,this))return;let t=e.params??{},r=e.currentTarget??e.target,n=Ye(t.ops),i=n.filter((a)=>Z(a,e,null)),o=i.length<=1?i:i.filter((a)=>Z(a,e,this.application?.schema?.keyMappings));if(o.length===0&&n.length>0){if(this.#$())this.#b((a)=>a.noBinding(this,e));return}for(let a of o){if(a.outside&&e[k])continue;if(!Xe(a,e))continue;if(Ze(this,e,a))continue;this.#ae(a.legacy?{...t,ops:a.ops}:a,e,r)}}#ae({ops:e,confirm:t,confirmWhen:r,outside:n,window:i},o,a){if(n&&this.element.contains(o.target))return;if(!i)o.preventDefault();let s=this.#z(t,r),c=()=>this.#Ae(this.#Te(e));if(!s)return c();if(s instanceof Promise)return s.then((l)=>l?this.#T(l,a,c):c());this.#T(s,a,c)}recompute(e){return m.get("compute")?.recompute(this,this.#e(),e)}tagsAdd(e){if(e?.defaultPrevented)return;if(this.#y(e).some((t)=>t.hasAttribute?.("data-reactive-highlighted")))return;return this.#g("tagsAdd",e)}tagsPick(e){return this.#g("tagsPick",e)}tagsRemove(e){return this.#g("tagsRemove",e)}nestedAdd(e){return this.#g("nestedAdd",e)}nestedRemove(e){return this.#g("nestedRemove",e)}syncNestedJson(e){return m.get("bindings")?.syncNestedJson(this,this.#e(),e)}#g(e,t){{let r=m.get("bindings");if(r)return r[e](this,this.#e(),t);t?.preventDefault?.();let n={currentTarget:t?.currentTarget??null,target:t?.target??null,preventDefault(){}};return b("bindings",(i)=>i[e](this,this.#e(),n))}}trackDirty(){(this.#t.get("form")??m.get("form"))?.scan(this,this.#e())}listnavNext(e){this.#A(e,(t,r)=>t<0?0:(t+1)%r)}listnavPrev(e){this.#A(e,(t,r)=>t<0?r-1:(t-1+r)%r)}listnavFirst(e){this.#A(e,()=>0)}listnavLast(e){this.#A(e,(t,r)=>r-1)}listnavPick(e){let t=this.#y(e),r=t.findIndex((n)=>n.hasAttribute("data-reactive-highlighted"));if(r<0)return;e.preventDefault(),t[r].click()}listnavClose(e){for(let t of this.#y(e))t.removeAttribute("data-reactive-highlighted")}#A(e,t){let r=this.#y(e);if(!r.length)return;e.preventDefault();let n=this.#ce(e),i=globalThis.document?.activeElement,o=n?r.findIndex((s)=>s===i||i!=null&&s.contains?.(i)):r.findIndex((s)=>s.hasAttribute("data-reactive-highlighted")),a=r[t(o,r.length)];if(n)a.focus?.();else{for(let s of r)s.removeAttribute("data-reactive-highlighted");a.setAttribute("data-reactive-highlighted","true")}a.scrollIntoView?.({block:"nearest"})}#ce(e){return(e?.currentTarget??e?.target??this.element).getAttribute?.("data-reactive-listnav-focus-param")==="true"}#y(e){let r=(e?.currentTarget??e?.target??this.element).getAttribute?.("data-reactive-listnav-option-param")??this.element.getAttribute("data-reactive-listnav-option-param");if(!r)return[];let n=this.#D();return Array.from(this.element.querySelectorAll(r)).filter((i)=>!i.hidden&&n(i))}#O(e,t,r,n,i,o,a){if(this.#i("reactive:before-dispatch",{action:t,params:this.#X(r),element:this.element},{cancelable:!0}).defaultPrevented)return;let c=Number(n)||0;if(c>0)return this.#le(e,c,t,r,o,a);let l=Number(i)||0;if(l>0)return this.#de(e,l,t,r,o,a);return this.#E(t,r,o,e,a)}#E(e,t,r,n,i){let o={inverse:null,undo:[],resurrect:null},a=this.#Fe(e,n,o),s=Boolean(r||i),c=(f)=>{if(!s||!f)return;let g=this.#e();if(o.inverse=f.optimistic(this,g,r,n),o.undo.push(...f.busy(this,g,i,n)),this.#C())o.resurrect=f.resurrection(this,g,r,n)},l=m.get("hints");if(!s||l)c(l);let h=()=>this.#ye(e,t,o,a),d=()=>this.#r?this.#r.then(h):h();return this.queue=(this.queue??Promise.resolve()).then(()=>s&&!l?this.#ue().then(c).then(d):d()),this.queue}#ue(){return this.#H("hints")}#le(e,t,r,n,i,o){this.#M(e);let a=()=>{this.#M(e),this.#E(r,n,i,e,o)},s=setTimeout(a,t);e?.addEventListener?.("blur",a,{once:!0}),this.#_.set(e,{timer:s,flush:a})}#M(e){let t=this.#_.get(e);if(!t)return;clearTimeout(t.timer),e?.removeEventListener?.("blur",t.flush),this.#_.delete(e)}#fe(){for(let e of[...this.#_.keys()])this.#M(e)}#de(e,t,r,n,i,o){let a=this.#u.get(e)??new Map;if(a.has(r))return;let s=setTimeout(()=>{if(a.delete(r),a.size===0)this.#u.delete(e)},t);return a.set(r,s),this.#u.set(e,a),this.#E(r,n,i,e,o)}#he(){for(let e of this.#u.values())for(let t of e.values())clearTimeout(t);this.#u.clear()}#i(e,t,{cancelable:r=!1}={}){let n=new CustomEvent(e,{bubbles:!0,composed:!0,cancelable:r,detail:t});return(this.element.isConnected?this.element:document).dispatchEvent(n),n}#s(e,t,r,n){let i=()=>{if(!this.element.isConnected){console.warn("[phlex-reactive] retry() ignored — the reactive root left the DOM");return}return this.#E(e,t)};this.#i("reactive:error",{action:e,params:r,...n,retry:i})}#n(e){if(this.element?.isConnected===!1)return;this.element?.setAttribute?.("data-reactive-error",e)}#me(){this.element?.removeAttribute?.("data-reactive-error")}#pe(){let e=document.querySelector("[data-reactive-error-flash]");if(!e?.content)return;let t=e.getAttribute("data-reactive-error-flash")||"flash",r=document.getElementById(t);if(!r)return;r.appendChild(e.content.cloneNode(!0))}#ge(){{let e=m.get("devtools");if(e)return e.delay();if(ae())return E("devtools").then((t)=>t.delay(),()=>{})}}#C(){return this.element?.getAttribute?.("data-reactive-debug")==="true"}#G(){return typeof performance<"u"&&typeof performance.now==="function"?performance.now():Date.now()}async#ye(e,t,r,n){let{fields:i,files:o}=this.#Y(),a=this.#X(t),s={...i,...a},c=this.#o,l=o.length>0,h=l?this.#we(c,e,s,o):JSON.stringify({token:c,act:e,params:s}),d=this.#C()?{action:e,paramNames:Object.keys(a),fieldNames:Object.keys(i),encoding:l?"multipart":"json",status:null,streams:[],tokenRefreshed:!1,started:this.#G()}:null;await this.#ge();try{if(navigator.onLine===!1){this.#a(r.inverse),this.#n("offline"),this.#s(e,t,s,{kind:"offline"});return}let f;try{let p={Accept:"text/vnd.turbo-stream.html","X-CSRF-Token":this.#Pe()};if(!l)p["Content-Type"]="application/json";let A=this.#Ie();if(A)p["X-Pgbus-Connection"]=A;he("action"),f=await fetch(this.#Re(),{method:"POST",headers:p,body:h,credentials:"same-origin",signal:AbortSignal.timeout(this.#xe())})}catch(p){if(console.error("[phlex-reactive] action error",p),this.#a(r.inverse),p?.name==="TimeoutError"||p?.name==="AbortError"){this.#n("timeout"),this.#s(e,t,s,{kind:"timeout"});return}this.#pe(),this.#n("network"),this.#s(e,t,s,{kind:"network"});return}if(d)d.status=f.status;if(f.redirected){console.error("[phlex-reactive] action was redirected (auth/CSRF?) — no update applied"),this.#a(r.inverse),this.#n("redirected"),this.#s(e,t,s,{kind:"redirected",status:f.status});return}if(!f.ok){let p=await f.text();if(console.error(`[phlex-reactive] action failed: HTTP ${f.status}`,p),this.#a(r.inverse),(f.headers.get("Content-Type")||"").includes("turbo-stream")){let A=this.#V(p);if(this.#o=A??this.#o,d)this.#b((Te)=>Te.recordBody(d,p,A));window.Turbo.renderStreamMessage(p)}this.#n("http"),this.#s(e,t,s,{kind:"http",status:f.status,body:p});return}let g=f.headers.get("Content-Type")||"";if(!g.includes("turbo-stream")){console.error(`[phlex-reactive] expected a turbo-stream, got "${g}" — no update applied`),this.#a(r.inverse),this.#n("content-type"),this.#s(e,t,s,{kind:"content-type",status:f.status});return}let y=await f.text(),T=this.#V(y);if(this.#o=T??this.#o,d)this.#b((p)=>p.recordBody(d,y,T));if(window.Turbo.renderStreamMessage(y),r.resurrect)queueMicrotask(r.resurrect);this.#me(),this.#i("reactive:applied",{action:e,params:s,html:y})}catch(f){console.error("[phlex-reactive] action error",f),this.#a(r.inverse),this.#i("reactive:error",{action:e,params:s,kind:"apply"})}finally{if(n?.(),d)this.#b((f)=>f.trace(this,{...d,ms:this.#G()-d.started}))}}get#o(){return this.#v??this.tokenValue}set#o(e){this.#v=e}tokenValueChanged(){this.#v=void 0}#V(e){let t=this.element.id;if(!t)return e.match(/data-reactive-token-value="([^"]+)"/)?.[1];let{token:r,self:n}=this.#be(t),i=e.match(r);if(i)return i[1];let o=e.match(n);if(o)return o[1].match(/data-reactive-token-value="([^"]+)"/)?.[1];return}#be(e){let t=this.#W;if(t&&t.id===e)return t;let r=Ce(e);return this.#W={id:e,token:new RegExp(`<turbo-stream\\b[^>]*\\baction="reactive:token"[^>]*\\btarget="${r}"[^>]*\\bdata-reactive-token-value="([^"]+)"`),self:new RegExp(`<turbo-stream\\b[^>]*\\baction="(?:replace|update)"[^>]*\\btarget="${r}"[^>]*>([\\s\\S]*?)</turbo-stream>`)}}#F(e){return e.closest('[data-controller~="reactive"]')===this.element}#D(){if(this.element.querySelectorAll('[data-controller~="reactive"]').length===0)return()=>!0;return(t)=>this.#F(t)}#Y(){let e={},t=[],r=this.#D(),n=[];this.element.querySelectorAll("input[name], select[name], textarea[name]").forEach((s)=>{if(!r(s))return;if(s.type==="file")for(let c of s.files??[])t.push({name:s.name,file:c,multiple:s.multiple});else n.push(s)});let i=[];this.element.querySelectorAll(`[name]${We}`).forEach((s)=>{if(r(s))i.push(s)});let o=this.#ve(n),a=this.#_e(n);for(let s of n)if(o.has(s.name)){let c=e[s.name]??(e[s.name]=[]);if(s.type==="checkbox"||s.type==="radio"){if(s.checked)c.push(s.value)}else if(s.type==="hidden"){if(!a.has(s.name))c.push(s.value)}else if(s.multiple&&s.options){for(let l of s.options)if(l.selected)c.push(l.value)}else c.push(s.value)}else if(s.type==="checkbox")e[s.name]=s.checked;else if(s.type==="radio"){if(s.checked)e[s.name]=s.value}else e[s.name]=s.value;return i.forEach((s)=>{let c=s.getAttribute("name");if(!c)return;let l=s.value??s.textContent??s.innerHTML??"",h=e[c];if(String(c).endsWith("[]")&&!Je(s))return;if(String(c).endsWith("[]")&&(h===void 0||Array.isArray(h))){(Array.isArray(h)?h:e[c]=[]).push(l);return}if(h==null||h==="")e[c]=l}),{fields:e,files:t}}#ve(e){let t=new Set;for(let r of e){if(r.type==="radio")continue;if(String(r.name).endsWith("[]"))t.add(r.name)}return t}#_e(e){let t=new Set;for(let r of e)if(r.type==="checkbox")t.add(r.name);return t}#we(e,t,r,n){let i=new FormData;i.append("token",e),i.append("act",t);let o=[];for(let[s,c]of Object.entries(r))if(Array.isArray(c)&&String(s).endsWith("[]"))if(c.length===0)o.push(String(s).slice(0,-2));else{let l=`${this.#k(s)}[]`;for(let h of c)i.append(l,String(h))}else this.#q(i,this.#k(s),c);for(let s of o)i.append("empty_groups[]",s);let a=this.#Se(n);for(let{name:s,file:c,multiple:l}of n){let d=l||a.has(s)?`${this.#k(s)}[]`:this.#k(s);i.append(d,c,c.name)}return i}#k(e){let t=String(e),r=t.indexOf("[");if(r===-1)return`params[${t}]`;return`params${[t.slice(0,r),...t.slice(r).match(/[^\[\]]+/g)??[]].map((i)=>`[${i}]`).join("")}`}#q(e,t,r){if(r==null)e.append(t,"");else if(Array.isArray(r))r.forEach((n,i)=>this.#q(e,`${t}[${i}]`,n));else if(typeof r==="object")for(let[n,i]of Object.entries(r))this.#q(e,`${t}[${n}]`,i);else e.append(t,String(r))}#Se(e){let t=new Map;for(let{name:r}of e)t.set(r,(t.get(r)??0)+1);return new Set([...t].filter(([,r])=>r>1).map(([r])=>r))}#X(e){if(!e)return{};try{return typeof e==="string"?JSON.parse(e):e}catch{return{}}}#Te(e){return j(e)}#Ae(e){q(e,(t)=>this.#j(t),(t,r)=>this.#B(`client op "${t}"`,r))}#$(){return this.element?.getAttribute?.("data-reactive-verbose")==="true"||this.#C()}#B(e,t){if(!this.#$())return;this.#b((r)=>r.diagnose(this,e,t))}#b(e){b("devtools",e)}#j(e){let t=e.to;if(t==="@root")return[this.element];if(typeof t!=="string"||t==="")return[];if(e.global)return[...document.querySelectorAll(t)];return[...this.element.querySelectorAll(t)].filter((r)=>this.#F(r))}#Ee(e,t){if(e?.checked!=="keep")return!1;let r=t?.type;return r==="checkbox"||r==="radio"}#a(e){if(!e)return;if(!this.element.isConnected)return;for(let t of e)t()}#Fe(e,t,r){this.#ke(e,t),ue();let n=!1;return()=>{if(n)return;n=!0,this.#Le(e,t),le();for(let i of r.undo)i()}}#ke(e,t){if(this.#c(t,e,1),this.#c(this.element,e,1),this.#l.set(e,(this.#l.get(e)??0)+1),this.#R++===0)this.element.setAttribute("aria-busy","true");for(let r of this.#Z(e))this.#c(r,e,1)}#Le(e,t){this.#c(t,e,-1),this.#c(this.element,e,-1);let r=(this.#l.get(e)??1)-1;if(r<=0)this.#l.delete(e);else this.#l.set(e,r);if(--this.#R<=0)this.#R=0,this.element.removeAttribute("aria-busy");for(let n of this.#Z(e))this.#c(n,e,-1)}#c(e,t,r){if(!e||typeof e.getAttribute!=="function")return;let n=this.#x.get(e)??new Map,i=(n.get(t)??0)+r;if(i<=0)n.delete(t);else n.set(t,i);if(n.size===0){this.#x.delete(e),e.removeAttribute("data-reactive-busy");return}this.#x.set(e,n),e.setAttribute("data-reactive-busy",[...n.keys()].join(" "))}#Z(e){return[...this.element.querySelectorAll?.("[data-reactive-busy-on]")??[]].filter((r)=>r.getAttribute("data-reactive-busy-on")===e&&this.#F(r))}#Re(){return this.#Q??=document.querySelector('meta[name="phlex-reactive-action-path"]')?.content||"/reactive/actions"}#xe(){if(this.#L!=null)return this.#L;let e=document.querySelector('meta[name="phlex-reactive-timeout"]')?.content,t=Number(e);return this.#L=Number.isFinite(t)&&t>0?t:30000}#Pe(){return document.querySelector('meta[name="csrf-token"]')?.content??""}#Ie(){return document.querySelector("pgbus-stream-source[connection-id]")?.getAttribute("connection-id")||document.querySelector('meta[name="pgbus-connection-id"]')?.content||null}}if(typeof window<"u")if(window.Turbo)G();else document.addEventListener("turbo:load",G,{once:!0});w("persist",()=>import("phlex/reactive/features/persist"),yt);w("defer",()=>import("phlex/reactive/features/defer"));w("form",()=>import("phlex/reactive/features/form"));w("bindings",()=>import("phlex/reactive/features/bindings"));w("compute",()=>import("phlex/reactive/features/compute"),bt);w("effects",()=>import("phlex/reactive/features/effects"));w("hints",()=>import("phlex/reactive/features/hints"));w("devtools",()=>import("phlex/reactive/features/devtools"));B();function yt(e,t){let r=()=>{t.edited=!0},n=(o)=>{let a=o.target;if(o.detail?.success&&a?.tagName==="FORM"&&a.contains?.(e))t.submitted=!0},i=["input","change","lexxy:change","trix-change"];for(let o of i)e.addEventListener?.(o,r);return document.addEventListener("turbo:submit-end",n),()=>{for(let o of i)e.removeEventListener?.(o,r);document.removeEventListener("turbo:submit-end",n)}}function bt(e,t){let r=()=>{t.edited=!0};return e.addEventListener?.("input",r),e.addEventListener?.("change",r),()=>{e.removeEventListener?.("input",r),e.removeEventListener?.("change",r)}}var vt=1000,S=null,Se,P=new Set;function _t(e){return ve(e).then((t)=>{if(e!=="effects")return;Se=t,t?.sweep()})}oe((e,t)=>{let r=e.detail,n=r?.render;if(typeof n!=="function")return;let i=r.newStream??e.target,o=be(wt(i)).filter((s)=>!P.has(s));if(!t&&!P.has("effects")&&St(i))o.push("effects");if(o.length===0&&!S)return t?.wrap(e);if(o.length>0){for(let c of o)P.add(c);let s=S=Promise.race([Promise.all([S,...o.map(_t)]),new Promise((c)=>setTimeout(c,vt))]).then(()=>{if(S!==s)return;S=null,P.clear()})}let a=S;r.render=async(s)=>{await a;let c={target:e.target,detail:{newStream:r.newStream,render:n}};(t??Se)?.wrap(c),await c.detail.render(s)}});function wt(e){let t=e?.querySelector?.("template")?.content;if(!t)return[];let r=[...t.querySelectorAll?.('[data-controller~="reactive"]')??[]];for(let n of t.children??[])if(n.matches?.('[data-controller~="reactive"]')&&!r.includes(n))r.unshift(n);return r}function St(e){return Boolean(e?.hasAttribute?.("data-reactive-effect")||document.getElementById?.(e?.getAttribute?.("target"))?.matches?.("[data-reactive-effect-update],[data-reactive-effect-exit]")||e?.querySelector?.("template")?.content?.querySelector?.("[data-reactive-effect-enter],[data-reactive-dismiss-after]")||document.querySelector?.("[data-reactive-dismiss-after]:not([data-reactive-dismiss-scheduled])"))}var Mt=we;export{ce as ACTIVE_ATTR,We as EDITOR_SELECTOR,Ue as EDITOR_TAGS,de as REQUESTS_ATTR,De as checkReactiveRegistration,he as countReactiveRequest,Mt as default,ue as enterReactiveActivity,Ce as escapeRegExp,le as exitReactiveActivity,ve as loadReactiveFeature,oe as onReactiveStreamWithoutEffects,Ft as reactiveActivityCount,Rt as reactiveFeatureNames,G as registerReactiveActions,Le as registerReactiveDefer,B as registerReactiveDev,Re as registerReactiveDismiss,Re as registerReactiveEffects,Lt as registerReactiveFeature,w as registerReactiveFeatureLoader,ke as registerReactiveJs,xe as registerReactiveMorphFocus,Ie as registerReactiveOffline,Re as registerReactiveStreamRender,Fe as registerReactiveToken,Ee as registerReactiveVisit,kt as resetReactiveActivity,be as unloadedFeaturesFor};
|
|
1
|
+
import{Controller as Le}from"@hotwired/stimulus";import{confirmResolver as H}from"phlex/reactive/confirm";function xe(){let e=window.Turbo?.StreamActions;if(!e||e["reactive:visit"])return;e["reactive:visit"]=function(){let t=this.getAttribute("data-url");if(t)window.Turbo.visit(t,{action:"advance"})}}function Re(){let e=window.Turbo?.StreamActions;if(!e||e["reactive:token"])return;e["reactive:token"]=function(){let t=this.getAttribute("data-reactive-token-value"),r=this.getAttribute("target");if(!t||!r)return;let n=document.getElementById(r);if(n)n.setAttribute("data-reactive-token-value",t)}}function Pe(){let e=window.Turbo?.StreamActions;if(!e||e["reactive:js"])return;e["reactive:js"]=function(){let t=K(this.getAttribute("data-reactive-ops"));if(!t.length)return;let r=this.getAttribute("target"),n=this.getAttribute("data-reactive-verbose")==="true",i=r?document.getElementById(r):null;if(r&&!i){if(n)y("devtools",(o)=>o.missingRoot(r));return}j(t,(o)=>it(o,i),n?(o,a)=>y("devtools",(s)=>s.diagnoseStream(o,a,i)):void 0)}}function Ie(){let e=window.Turbo?.StreamActions;if(!e||e["reactive:defer"])return;e["reactive:defer"]=function(){y("defer",(t)=>t.streamAction(this))}}var V=!1,W=null;function Ne(){if(V)return;if(typeof document>"u"||typeof document.addEventListener!=="function")return;V=!0,document.addEventListener("turbo:before-stream-render",(e)=>{if(W)W(e,m.get("effects"));else m.get("effects")?.wrap(e)})}function ue(e){W=e}var G=!1;function O(e){return e===document.activeElement&&(e.localName==="input"||e.localName==="textarea"||e.hasAttribute("data-reactive-editable"))&&!e.hasAttribute("data-reactive-morph-value")&&!!e.closest('[data-controller~="reactive"]')}function Oe(){if(G)return;if(typeof document>"u"||typeof document.addEventListener!=="function")return;G=!0,document.addEventListener("turbo:before-morph-element",(e)=>{let t=e.target,r=e.detail?.newElement;if(!r||e.defaultPrevented)return;if(t.hasAttribute?.("data-reactive-editable")){if(R.set(t,F(r)),!O(t))return;for(let{name:i}of[...t.attributes])if(!r.hasAttribute(i)&&i!=="contenteditable")t.removeAttribute(i);for(let{name:i,value:o}of r.attributes)if(i!=="contenteditable"&&t.getAttribute(i)!==o)t.setAttribute(i,o);return e.preventDefault()}if(t.localName!=="input"||!O(t))return;let n=r.getAttribute("value");if(n==null)t.removeAttribute("value");else if(t.getAttribute("value")!==n)t.setAttribute("value",n)}),document.addEventListener("focusin",(e)=>J(e.target)),document.addEventListener("turbo:before-morph-attribute",(e)=>{if(e.detail?.attributeName==="value"&&O(e.target))e.preventDefault()})}var Me="__materialize",Y=!1;function Ce(){if(Y)return;if(typeof window>"u"||typeof document>"u")return;if(typeof window.addEventListener!=="function")return;Y=!0;let e=()=>{let t=document.documentElement;if(typeof t?.toggleAttribute!=="function")return;t.toggleAttribute("data-reactive-offline",globalThis.navigator?.onLine===!1)};e(),window.addEventListener("online",e),window.addEventListener("offline",e)}var De="phlex-reactive:latency";function le(){return typeof sessionStorage<"u"&&Boolean(sessionStorage.getItem(De))}function U(){if(typeof document>"u")return;if(!m.has("devtools")&&!_.get("devtools")[1])return;let e=document.querySelector?.('meta[name="phlex-reactive-env"]')?.content==="development";if(e||le())y("devtools",(t)=>e&&t.attach())}var de="data-reactive-active",S=0;function fe(){if(S++,S===1)me("reactive:busy")}function he(){if(S===0)return;if(S--,S===0)me("reactive:idle")}function Pt(){return S}function It(){S=0,(typeof document<"u"?document.documentElement:null)?.removeAttribute?.(de)}function me(e){if(typeof document>"u")return;let t=document.documentElement;if(typeof t?.toggleAttribute==="function")t.toggleAttribute(de,S>0);if(typeof document.dispatchEvent==="function"&&typeof CustomEvent==="function")document.dispatchEvent(new CustomEvent(e,{detail:{count:S}}))}var pe="data-reactive-requests";function ge(e){if(typeof document>"u")return;let t=document.documentElement;if(typeof t?.setAttribute!=="function"||!qe(t))return;let r=$e(t);r[e]=(Number(r[e])||0)+1,t.setAttribute(pe,JSON.stringify(r))}function qe(e){return e.hasAttribute?.("data-reactive-verbose")||!!document.querySelector?.('[data-reactive-verbose="true"]')}function $e(e){try{let t=JSON.parse(e.getAttribute(pe));if(t&&typeof t==="object")return{action:0,defer:0,...t}}catch{}return{action:0,defer:0}}function X(){xe(),Re(),Pe(),Ie(),Ne(),Oe(),Ce(),U()}function Be(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}var be=Symbol.for("phlex-reactive.core");if(globalThis[be])console.error("[phlex-reactive] the client was loaded twice. Import EITHER phlex/reactive/reactive_controller (the default, "+"everything in one file) OR phlex/reactive/core (features on demand), not both — each carries its own state.");globalThis[be]=!0;var ye=!1;function We(){if(ye)return;if(typeof document>"u")return;let e=document.querySelectorAll('[data-controller~="reactive"]');if(!e||e.length===0)return;console.warn("[phlex-reactive] found "+e.length+' element(s) with data-controller="reactive" '+"but the reactive controller never connected. It is loaded but not registered — "+'add `application.register("reactive", ReactiveController)` (importmap) or import it into app/javascript/controllers/ for lazyLoadControllersFrom apps. See the README.')}if(typeof window<"u"&&typeof document<"u"){let e=()=>setTimeout(We,0);if(document.readyState==="loading")document.addEventListener("DOMContentLoaded",e,{once:!0});else e()}var je=new Set(["href","src","srcdoc","action","formaction","xlink:href","style"]);function Ue(e){let t=String(e).toLowerCase();return t.startsWith("on")||je.has(t)}var M=new WeakMap;function Je(e,t,r){let[n,i,o]=t;M.get(e)?.(),e.classList.add(n,i),r();let a=!1,s=requestAnimationFrame(()=>{if(a)return;e.classList.remove(i),e.classList.add(o)}),c=(h)=>{if(a||h&&h.target!==e)return;a=!0,M.delete(e),clearTimeout(l),globalThis.cancelAnimationFrame?.(s),e.removeEventListener("animationend",c),e.removeEventListener("transitionend",c),e.classList.remove(n,i,o)};M.set(e,c),e.addEventListener("animationend",c),e.addEventListener("transitionend",c);let l=setTimeout(c,Ke(e))}function Ke(e){let t=0;try{let r=getComputedStyle(e);for(let n of["transition","animation"]){let[i,o]=["Duration","Delay"].map((s)=>String(r[n+s]).split(",").map((c)=>parseFloat(c)*(/ms/.test(c)?1:1000)||0)),a=String(r[n+"IterationCount"]).split(",").map((s)=>/inf/.test(s)?1/0:parseFloat(s)>=0?parseFloat(s):1);for(let s=0;s<i.length||s<o.length||s<a.length;s++){let c=i[s%i.length]*a[s%a.length]||0;t=Math.max(t,c+o[s%o.length])}}}catch{}return Math.min(Math.max(t+50,350),5000)}var ze=":is(lexxy-editor, trix-editor, [contenteditable=''], [contenteditable=true], [contenteditable=plaintext-only])",He=new Set(["lexxy-editor","trix-editor"]);function F(e){let t="",r=!1,n=(o)=>{if(r&&t&&!t.endsWith(`
|
|
2
|
+
`))t+=`
|
|
3
|
+
`;r=!1,t+=o},i=(o)=>{for(let a of o.childNodes){let s=/^(div|p|li)$/.test(a.localName);if(s)r=!0;if(a.nodeType===3)a.data&&n(a.data);else if(a.localName==="br")n(`
|
|
4
|
+
`);else if(a.nodeType===1)i(a);if(s)r=!0}};return i(e),t=t.replace(/\r\n?/g,`
|
|
5
|
+
`).replace(/\n$/,""),e.getAttribute("data-reactive-editable")==="multi"?t:t.replace(/\n/g," ")}var R=new WeakMap;function J(e){if(e?.hasAttribute?.("data-reactive-editable")&&!R.has(e))R.set(e,F(e))}function ve(e){return J(e),R.get(e)}function Ve(e){return He.has(e.localName)?typeof e.value==="string":!0}var Z=Object.freeze({show:(e,t,r)=>C(e,!1,t,r),hide:(e,t,r)=>C(e,!0,t,r),toggle:(e,t,r)=>C(e,!e.hidden,t,r),add_class:(e,t)=>e.classList.add(...t.classes??[]),remove_class:(e,t)=>e.classList.remove(...t.classes??[]),toggle_class:(e,t)=>(t.classes??[]).forEach((r)=>e.classList.toggle(r)),set_attr:(e,t)=>{if(D(t.name))e.setAttribute(t.name,t.value??"")},remove_attr:(e,t)=>{if(D(t.name))e.removeAttribute(t.name)},toggle_attr:(e,t)=>{if(!D(t.name))return;if(Array.isArray(t.values)){let[r,n]=t.values;e.setAttribute(t.name,e.getAttribute(t.name)===r?n:r)}else if(e.hasAttribute(t.name))e.removeAttribute(t.name);else e.setAttribute(t.name,"")},focus:(e)=>e.focus?.(),focus_first:(e)=>Qe(e)?.focus?.(),text:(e,t)=>{let r=String(t.value??"");if(e.textContent!==r)e.textContent=r},check_group:(e,t)=>{let r=t.global?document.querySelectorAll('[data-controller~="reactive"]'):[e];for(let n of r)y("bindings",(i)=>i.checkGroup(n,t.group,t.checked))},dispatch:(e,t)=>{e.dispatchEvent(new CustomEvent(t.name,{bubbles:!0,composed:!0,detail:t.detail??{}}))},submit:(e,t,r)=>{let n=Ge(e);if(!n)return;let i=null;if(t?.submitter!=null&&typeof r==="function"){if(i=r({...t,to:t.submitter})[0]??null,!(i?.form===n&&(i.type==="submit"||i.type==="image")))console.warn(`[phlex-reactive] submitter ${t.submitter} is not a submit control of the form — ignored`),i=null}i?n.requestSubmit?.(i):n.requestSubmit?.()},paste_into:(e)=>Ye(e),persist_state:(e,t)=>y("persist",(r)=>r.writeState(e,t.state)),persist_clear:(e)=>y("persist",(t)=>t.clearRoot(e))});function Ge(e){if(e?.tagName==="FORM")return e;return e?.form??e?.closest?.("form")??null}function Ye(e){let t=globalThis.navigator?.clipboard;if(typeof t?.readText!=="function")return;t.readText().then((r)=>{if(!r)return;if(e.value=r,typeof e.dispatchEvent==="function")e.dispatchEvent(new Event("input",{bubbles:!0}));e.focus?.()}).catch(()=>{})}function Xe(e,t,r){if(r?.transition)Je(e,r.transition,()=>e.hidden=t);else e.hidden=t}function C(e,t,r,n){if(Xe(e,t,r),r?.expanded==null||typeof n!=="function")return;for(let i of n({...r,to:r.expanded}))i.setAttribute?.("aria-expanded",String(!t))}function D(e){if(!Ue(e))return!0;return console.warn(`[phlex-reactive] refused client attr op on ${JSON.stringify(e)} — skipped`),!1}var Ze='a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';function Qe(e){return e.querySelectorAll?.(Ze)?.[0]??null}function K(e){if(Array.isArray(e))return e;if(typeof e!=="string")return[];try{let t=JSON.parse(e);return Array.isArray(t)?t:[]}catch{return[]}}var Q=Object.freeze({enter:"Enter",tab:"Tab",esc:"Escape",space:" ",up:"ArrowUp",down:"ArrowDown",left:"ArrowLeft",right:"ArrowRight",home:"Home",end:"End",page_up:"PageUp",page_down:"PageDown"}),ee=Object.freeze(["meta","ctrl","alt","shift"]);function z(e,t,r){let n=e.split("+");for(let a of ee)if(n.includes(a)!==Boolean(t[`${a}Key`]))return!1;let i=n.find((a)=>!ee.includes(a));if(i===void 0||typeof t.key!=="string")return!0;let o=r&&Object.hasOwn(r,i)?r[i]:Object.hasOwn(Q,i)?Q[i]:/^[a-z0-9]$/.test(i)?i:null;return typeof o==="string"&&o.toLowerCase()===t.key.toLowerCase()}function et(e){if(Array.isArray(e))return[{ops:e,legacy:!0}];if(e&&typeof e==="object")return[e];if(typeof e!=="string")return[];let t=e.trim();if(t.startsWith("["))return[{ops:K(t),legacy:!0}];let r=[];for(let n of t.split(" ")){if(n==="")continue;let i=null;try{i=JSON.parse(n)}catch{i=null}if(i&&typeof i==="object"&&!Array.isArray(i))r.push(i);else console.warn(`[phlex-reactive] malformed on_client binding record ${JSON.stringify(n)} — skipped`)}return r}function te(e,t,r){if(e.legacy)return!0;let n=e.on;if(typeof n!=="string"||n==="")return!1;let i=n.indexOf(".");if(t.type!==(i<0?n:n.slice(0,i)))return!1;if(Boolean(e.window)!==Te(t.currentTarget))return!1;return i<0||r===null||z(n.slice(i+1),t,r)}function tt(e,t){let r=e.detail;if(r===void 0)return!0;if(r===null||typeof r!=="object"||Array.isArray(r))return!1;let n=t.detail;if(n===null||typeof n!=="object")return!1;return Object.keys(r).every((i)=>n[i]===r[i])}var re=new WeakMap;function rt(e,t,r){if(!r.once)return!1;let n=t.currentTarget??e.element,i=re.get(e);if(!i)i=new WeakMap,re.set(e,i);let o=i.get(n);if(!o)o=new Set,i.set(n,o);let a=t.params?.ops,c=(n===globalThis.window?`${typeof a==="string"?a:JSON.stringify(a??null)}|`:"")+JSON.stringify(r);if(o.has(c))return!0;return o.add(c),!1}var ne=new WeakMap;function nt(e,t){if(e===null||typeof e!=="object")return!1;let r=e.params?.ops,n=typeof r==="string"?r:JSON.stringify(r??null),i=ne.get(e);if(!i)i=new WeakMap,ne.set(e,i);let o=i.get(t);if(!o)o=new Map,i.set(t,o);let a=o.get(e.currentTarget);if(!a)a=new Set,o.set(e.currentTarget,a);if(a.has(n))return!0;return a.add(n),!1}function j(e,t,r){for(let n of e){if(!Array.isArray(n))continue;let[i,o={}]=n;if(!Object.hasOwn(Z,i)){console.warn(`[phlex-reactive] unknown client op ${JSON.stringify(i)} — skipped`);continue}let a=t(o);if(a.length===0&&r)r(i,o);for(let s of a)Z[i](s,o,t)}}function it(e,t){let r=e.to;if(t){if(r==="@root")return[t];if(typeof r!=="string"||r==="")return[];if(e.global)return[...document.querySelectorAll(r)];return[...t.querySelectorAll(r)]}if(typeof r!=="string"||r===""||r==="@root")return[];return[...document.querySelectorAll(r)]}function st(e){let t=e.getAttribute?.("data-reactive-persist");return Boolean(t)&&t!=="off"}function _e(e){return["defer-token","defer-src","lazy-on","lazy-visible"].some((t)=>e.getAttribute?.(`data-reactive-${t}`)!=null)}function ot(e){return(e.getAttribute?.("data-action")??"").includes("reactive#trackDirty")||e.getAttribute?.("data-reactive-clipboard")!=null||e.querySelector?.('[data-action*="reactive#trackDirty"],[data-reactive-clipboard]')!=null}function at(e){return["show-targets","group-targets","on-complete","filter-input","tags-field"].some((t)=>e.getAttribute?.(`data-reactive-${t}`)!=null)||(e.querySelectorAll?.("[data-reactive-show-field], [data-reactive-show]")??[]).length>0||e.querySelector?.("[data-reactive-nested-json], [data-reactive-nested-list], [data-reactive-confirm-when-param], [data-reactive-enable], [data-reactive-select-all], [data-reactive-count], [data-reactive-editable]")!=null}function ct(e){return e.getAttribute?.("data-reactive-compute-inputs-param")!=null}function ut(e){return Boolean(e.matches?.("[data-reactive-sortable],[data-reactive-sortable-source]")||e.querySelector?.("[data-reactive-sortable],[data-reactive-sortable-source]"))}var lt=[["persist",[st,null,null,!0]],["defer",[_e,null]],["form",[ot,null]],["bindings",[at,null,null,!0]],["compute",[ct,null,null,!0]],["sortable",[ut,null]],["effects",[(e)=>["enter","update","exit"].some((t)=>e.getAttribute?.(`data-reactive-effect-${t}`)!=null),null]],["hints",[(e)=>e.querySelector?.("[data-reactive-optimistic-param], [data-reactive-busy-param], [data-reactive-loading-param]")!=null,null]],["devtools",[(e)=>e.getAttribute?.("data-reactive-debug")==="true",null]]],_=new Map(lt),ie=new Map,m=new Map,dt=new Map;function v(e,t,r){Se.set(e,[t,r]),ft(e)}var Se=new Map;function ft(e){let t=_.get(e),[r,n]=Se.get(e);t[1]=r,t[2]=n}function Nt(e,t){t.install?.(k),m.set(e,t),dt.set(e,t)}function we(e){let t=[];for(let[r,[n]]of _){if(!n||m.has(r))continue;try{if(e.some((i)=>n(i)))t.push(r)}catch{}}return t}function Ae(e){return E(e).catch((t)=>P(e,"load",t))}function E(e){let t=ie.get(e);if(!t)t=(_.get(e)[1]??(()=>Promise.reject(Error("this entry has no way to import it"))))().then((n)=>(n.install?.(k),m.set(e,n),n)),ie.set(e,t);return t}var se=new Set;function P(e,t,r){let n=e+t;if(se.has(n))return;se.add(n),console.error(`[phlex-reactive] the "${e}" feature module (phlex/reactive/features/${e}) is unavailable: ${t}. If you bundle or vendor the client, alias or pin phlex/reactive/features/* (README: esbuild / webpack / bun).`,r)}function y(e,t){let r=m.get(e);if(r)return t(r);E(e).then(t,(n)=>P(e,"load",n))}var ht=1e4;function q(){let e=Number(globalThis.document?.head?.querySelector?.('meta[name="phlex-reactive-feature-timeout"]')?.content);return Number.isFinite(e)&&e>0?e:ht}function Ot(){return[..._.keys()]}var oe=Promise.resolve();function ae(e,t,r){let n=k.waiting.get(e);if(n&&n[t]===r)delete n[t]}var k={enter:fe,exit:he,count:ge,text:F,served:ve,waiting:new WeakMap};var L=Symbol.for("phlex-reactive.early"),mt=1e4,pt=1500,Te=(e)=>e!=null&&(e===globalThis.window||e.window===e);function B(){return globalThis[L]??={queue:[],connected:new WeakSet},globalThis[L]}function gt(){let e=Number(globalThis.document?.querySelector?.('meta[name="phlex-reactive-early-ttl"]')?.content);return Number.isFinite(e)&&e>0?e:mt}function bt(e){let t={};for(let{name:r,value:n}of Array.from(e.attributes??[])){let i=/^data-reactive-(.+)-param$/i.exec(r);if(!i)continue;let o;try{o=JSON.parse(n)}catch{o=n}t[i[1].replace(/[-_]([a-z0-9])/g,(a,s)=>s.toUpperCase())]=o}return t}function yt(e,t,r){return{[L]:!0,type:e.type,detail:e.detail,target:e.target,currentTarget:r?t.ownerDocument.defaultView:t,params:bt(t),key:e.key,code:e.code,metaKey:e.metaKey,ctrlKey:e.ctrlKey,altKey:e.altKey,shiftKey:e.shiftKey,button:e.button,submitter:e.submitter,bubbles:e.bubbles,timeStamp:e.timeStamp,defaultPrevented:e.defaultPrevented,preventDefault(){},stopPropagation(){},stopImmediatePropagation(){}}}var I=new WeakMap;function vt(e,{token:t,type:r,method:n,filter:i,win:o},a){let s=I.get(e);if(!s)I.set(e,s=new Map);let c=o?`${t} ${a}`:t;if(s.has(c))return!1;return s.set(c,{type:r,method:n,filter:i,win:o,params:a,armed:!0}),!0}function ce(e,t,r,n){if(e?.[L])return!1;let i=Te(e?.currentTarget),o=i?n:e?.currentTarget,a=St(e,o,i?`@${t}`:t),s=I.get(o),c=i&&JSON.stringify(e.params);for(let l of s?.values()??[]){if(!l.armed||l.method!==t||l.type!==e.type||Boolean(l.win)!==i)continue;if(i&&l.params!==c)continue;if(l.filter&&!z(l.filter,e,r))continue;return l.armed=!1,!0}return a}var x=new WeakMap;function _t(e,t,r){let n=x.get(e);if(!n)x.set(e,n=new Map),setTimeout(()=>x.delete(e));let i=n.get(t)??{};n.set(t,i),i[r]=(i[r]??0)+1}function St(e,t,r){let n=e&&x.get(e)?.get(t);if(!n?.[r])return!1;return n[r]-=1,!0}class Ee extends Le{static values={token:String};#_;#S=new Map;#l=new Map;#Q;#L;#j;#x=0;#d=new Map;#R=new WeakMap;#i;featuresReady=oe;#t=new Map;#P=new Set;#f=!1;#r=null;#h=new Map;#w=new Map;#m=new Map;#p=0;#U;#Ne=0;#Oe=0;connect(){if(ye=!0,B().connected.add(this.element),this.element.id==="")console.warn("[phlex-reactive] a reactive root has no id; its next-action token can't self-match "+"and may fall back to the first token in the response → a silent HTTP 403 on the NEXT action. "+"Put id: on the SAME element as reactive_attrs — use div(**reactive_root) (emits id + token together), "+"or div(id:, **reactive_attrs). The id: must NOT be on a child. See the README.");if(this.element.getAttribute?.("data-reactive-token-value")!=null||_e(this.element))this.#i=(e)=>{if(e.type==="focusin"?!e.target.hasAttribute?.("data-reactive-editable"):e.target!==this.element)return;this.#J(!0)},this.element.addEventListener?.("turbo:morph-element",this.#i),this.element.addEventListener?.("focusin",this.#i);this.element.querySelectorAll?.("[data-reactive-editable]").forEach(J),this.#p++,this.featuresReady=oe,this.#J(),this.#ie()}#Me(e){}#Ce(e,t,r,n){}#De(){}#qe(e,t){}#J(e){{let t=this.#p,r=[];for(let[o,[a]]of _){if(!a||this.#P.has(o))continue;let s=m.get(o);if(s&&!s.connect)continue;try{if(a(this.element))r.push(o)}catch(c){this.#g(o,"detect",c)}}if(r.length===0)return;for(let o of r)this.#P.add(o);if(!this.#f&&r.every((o)=>m.has(o))){for(let o of r)this.#I(t,o,m.get(o),e);return}for(let o of r){let a=this.#ee(t,o),s=[];for(let l of _.keys()){if(l===o)break;if(this.#m.has(l))s.push(this.#m.get(l))}let c=Promise.all(s).then(()=>a).then((l)=>l&&this.#I(t,o,l,e));if(this.#m.set(o,c),_.get(o)[3])this.#r=c}let n=this.#r;n?.then(()=>{if(this.#r===n)this.#r=null}),this.#f=!0;let i=Promise.all(this.#m.values()).then(()=>{if(this.featuresReady===i)this.#f=!1});this.featuresReady=i}}#ee(e,t){{let r=()=>e===this.#p;return this.#te(t),new Promise((n)=>{let i=!1,o=setTimeout(()=>{if(this.#h.delete(o),i=!0,r())this.#g(t,"timeout",Error(`not loaded after ${q()} ms`));n(null)},q());this.#h.set(o,n);let a=()=>{clearTimeout(o),this.#h.delete(o)};E(t).then((s)=>{if(a(),i)queueMicrotask(()=>this.#I(e,t,s));else n(s)},(s)=>{if(a(),r())this.#g(t,"load",s);n(null)})})}}#te(e){{let t=_.get(e)[2];if(!t)return;let r={};try{this.#w.set(e,[t(this.element,r),r]),k.waiting.set(this.element,{...k.waiting.get(this.element),[e]:r})}catch(n){this.#g(e,"detect",n)}}}#K(e,t){{let[r,n]=this.#w.get(e)??[];if(this.#w.delete(e),!t)ae(this.element,e,n);try{r?.()}catch(i){console.error(`[phlex-reactive] the "${e}" feature module failed to stop waiting`,i)}return n}}#I(e,t,r,n){{if(e!==this.#p)return;let i=this.#K(t);try{r.connect?.(this,this.#e(),n,i),this.#t.set(t,r)}catch(o){this.#g(t,"connect",o)}}}#e(){return this.#U??={emit:(e,t,r)=>this.#s(e,t,r),reseed:()=>this.#re(),proceed:(e,t,r,n)=>this.#O(e,t,r,0,0,null,n),forgetToken:()=>{this.#_=void 0},owns:(e)=>this.#F(e),ownership:()=>this.#D(),opTargets:(e)=>this.#W(e),diagnose:(e,t)=>this.#B(e,t),applyOps:(e,t)=>j(e,(r)=>this.#W(t!=null&&r.to==null?{...r,to:t}:r),(r,n)=>this.#B(`client op "${r}"`,n)),listnavOptions:(e)=>this.#y(e),collectFields:()=>this.#Y(),text:F,served:ve,confirm:(e,t)=>Promise.resolve().then(()=>H(e,t)).catch(()=>!1)},this.#U}#re(){this.#t.get("form")?.scan(this,this.#e()),this.#t.get("bindings")?.reseed(this),this.#t.get("compute")?.seed(this)}#g(e,t,r){if(P(e,t,r),this.#n("feature"),t==="load"&&this.element.getAttribute?.("data-reactive-defer-pending"))this.element.removeAttribute("data-reactive-defer-pending"),this.element.removeAttribute("aria-busy");this.#s("reactive:error",{kind:"feature",feature:e,phase:t,error:r})}#ne(){{this.#p++;for(let[t,r]of this.#h)clearTimeout(t),r(null);this.#h.clear();for(let t of[...this.#w.keys()]){let r=this.#K(t,!0),n=this.element;y(t,(i)=>{i.abandon?.(n,r),ae(n,t,r)})}this.#P.clear(),this.#m.clear(),this.#f=!1,this.#r=null;let e=[...this.#t];this.#t.clear();for(let[t,r]of e)try{r.disconnect?.(this,this.#e())}catch(n){console.error(`[phlex-reactive] the "${t}" feature module failed to disconnect`,n)}}}#ie(){if(this.element.setAttribute?.("data-reactive-connected",""),this.element.isConnected)this.element.dispatchEvent?.(new CustomEvent("reactive:connect",{bubbles:!0,composed:!0,detail:{id:this.element.id}}));this.#se()}#N=new Set;#se(){let{queue:e}=B();if(e.length===0)return;let t=[];for(let i=e.length-1;i>=0;i--){let{root:o}=e[i];if(o===this.element||!o.isConnected)t.unshift(...e.splice(i,1))}let r=gt(),n=new Map;for(let i of t){let o=n.get(i.event)??[new Set,new Set];n.set(i.event,o);let a=o[+!!i.win];if(a.has(i.el))continue;a.add(i.el);let s=i.win?Math.min(pt,r):r,c=i.root!==this.element?"its root left the page before a controller connected":performance.now()-i.at>s?`it is older than the ${s} ms ${i.win?"window-trigger":"early-event"} TTL`:i.el.isConnected&&this.element.contains(i.el)?null:"its element left the root before the controller connected";if(c){if(this.#$())console.warn(`[phlex-reactive] dropped an early "${i.event.type}" trigger: ${c}`);continue}this.#oe(i)}}#oe({event:e,el:t,descs:r,win:n}){let i=yt(e,t,n),o=this.application?.schema?.keyMappings,a=(t.getAttribute("data-action")??"").split(/\s+/),s=n?this.element:t;for(let c of r){if(!a.includes(c.token))continue;if(c.filter&&!z(c.filter,e,o))continue;if(/#\w+.*:once\b/.test(c.token)){if(!vt(s,c,n&&JSON.stringify(i.params)))continue;this.#N.add(s)}if(_t(e,s,n?`@${c.method}`:c.method),c.method==="runOps")this.runOps(i);else this.dispatch(i)}}disconnect(){if(this.#ne(),this.#de(),this.#he(),this.#i)this.element.removeEventListener?.("turbo:morph-element",this.#i),this.element.removeEventListener?.("focusin",this.#i);B().connected.delete(this.element),this.element.removeAttribute?.("data-reactive-connected");for(let e of this.#N)I.delete(e);this.#N.clear()}dispatch(e){if(ce(e,"dispatch",this.application?.schema?.keyMappings,this.element))return;let{action:t,params:r,debounce:n,throttle:i,confirm:o,confirmWhen:a,outside:s,window:c,optimistic:l}=e.params;if(!t)return;let h=e.params.busy??e.params.loading;if(s&&this.element.contains(e.target))return;let f=e.currentTarget??e.target;if(!c&&!this.#Ee(l,f))e.preventDefault();if(t===Me){let b=()=>{{let A=this.#t.get("defer");if(A)return A.materialize(this)}if(this.tokenValue)return this.#O(f,t,"{}")};return this.#f?this.featuresReady.then(b):b()}let d=this.#z(o,a),g=()=>this.#O(f,t,r,n,i,l,h);if(!d)return g();if(d instanceof Promise)return d.then((b)=>b?this.#A(b,f,g):g());this.#A(d,f,g)}#A(e,t,r){return Promise.resolve().then(()=>H(e,{el:t})).catch(()=>!1).then((n)=>{if(n)r()})}#z(e,t){if(e)return e;if(!t)return null;{let r=m.get("bindings");if(r)return r.confirmMessage(this,this.#e(),t);return this.#H("bindings").then((n)=>n&&n.confirmMessage(this,this.#e(),t))}}#H(e){{let t;return Promise.race([E(e).catch((r)=>(P(e,"load",r),null)),new Promise((r)=>t=setTimeout(()=>r(null),q()))]).finally(()=>clearTimeout(t))}}runOps(e){if(ce(e,"runOps",this.application?.schema?.keyMappings,this.element))return;if(nt(e,this))return;let t=e.params??{},r=e.currentTarget??e.target,n=et(t.ops),i=n.filter((a)=>te(a,e,null)),o=i.length<=1?i:i.filter((a)=>te(a,e,this.application?.schema?.keyMappings));if(o.length===0&&n.length>0){if(this.#$())this.#v((a)=>a.noBinding(this,e));return}for(let a of o){if(a.outside&&e[L])continue;if(!tt(a,e))continue;if(rt(this,e,a))continue;this.#ae(a.legacy?{...t,ops:a.ops}:a,e,r)}}#ae({ops:e,confirm:t,confirmWhen:r,outside:n,window:i},o,a){if(n&&this.element.contains(o.target))return;if(!i)o.preventDefault();let s=this.#z(t,r),c=()=>this.#Te(this.#Ae(e));if(!s)return c();if(s instanceof Promise)return s.then((l)=>l?this.#A(l,a,c):c());this.#A(s,a,c)}recompute(e){return m.get("compute")?.recompute(this,this.#e(),e)}tagsAdd(e){if(e?.defaultPrevented)return;if(this.#y(e).some((t)=>t.hasAttribute?.("data-reactive-highlighted")))return;return this.#b("tagsAdd",e)}tagsPick(e){return this.#b("tagsPick",e)}tagsRemove(e){return this.#b("tagsRemove",e)}nestedAdd(e){return this.#b("nestedAdd",e)}nestedRemove(e){return this.#b("nestedRemove",e)}syncNestedJson(e){return m.get("bindings")?.syncNestedJson(this,this.#e(),e)}#b(e,t){{let r=m.get("bindings");if(r)return r[e](this,this.#e(),t);t?.preventDefault?.();let n={currentTarget:t?.currentTarget??null,target:t?.target??null,preventDefault(){}};return y("bindings",(i)=>i[e](this,this.#e(),n))}}trackDirty(){(this.#t.get("form")??m.get("form"))?.scan(this,this.#e())}listnavNext(e){this.#T(e,(t,r)=>t<0?0:(t+1)%r)}listnavPrev(e){this.#T(e,(t,r)=>t<0?r-1:(t-1+r)%r)}listnavFirst(e){this.#T(e,()=>0)}listnavLast(e){this.#T(e,(t,r)=>r-1)}listnavPick(e){let t=this.#y(e),r=t.findIndex((n)=>n.hasAttribute("data-reactive-highlighted"));if(r<0)return;e.preventDefault(),t[r].click()}listnavClose(e){for(let t of this.#y(e))t.removeAttribute("data-reactive-highlighted")}#T(e,t){let r=this.#y(e);if(!r.length)return;e.preventDefault();let n=this.#ce(e),i=globalThis.document?.activeElement,o=n?r.findIndex((s)=>s===i||i!=null&&s.contains?.(i)):r.findIndex((s)=>s.hasAttribute("data-reactive-highlighted")),a=r[t(o,r.length)];if(n)a.focus?.();else{for(let s of r)s.removeAttribute("data-reactive-highlighted");a.setAttribute("data-reactive-highlighted","true")}a.scrollIntoView?.({block:"nearest"})}#ce(e){return(e?.currentTarget??e?.target??this.element).getAttribute?.("data-reactive-listnav-focus-param")==="true"}#y(e){let r=(e?.currentTarget??e?.target??this.element).getAttribute?.("data-reactive-listnav-option-param")??this.element.getAttribute("data-reactive-listnav-option-param");if(!r)return[];let n=this.#D();return Array.from(this.element.querySelectorAll(r)).filter((i)=>!i.hidden&&n(i))}#O(e,t,r,n,i,o,a){if(this.#s("reactive:before-dispatch",{action:t,params:this.#X(r),element:this.element},{cancelable:!0}).defaultPrevented)return;let c=Number(n)||0;if(c>0)return this.#le(e,c,t,r,o,a);let l=Number(i)||0;if(l>0)return this.#fe(e,l,t,r,o,a);return this.#E(t,r,o,e,a)}#E(e,t,r,n,i){let o={inverse:null,undo:[],resurrect:null},a=this.#Fe(e,n,o),s=Boolean(r||i),c=(d)=>{if(!s||!d)return;let g=this.#e();if(o.inverse=d.optimistic(this,g,r,n),o.undo.push(...d.busy(this,g,i,n)),this.#C())o.resurrect=d.resurrection(this,g,r,n)},l=m.get("hints");if(!s||l)c(l);let h=()=>this.#be(e,t,o,a),f=()=>this.#r?this.#r.then(h):h();return this.queue=(this.queue??Promise.resolve()).then(()=>s&&!l?this.#ue().then(c).then(f):f()),this.queue}#ue(){return this.#H("hints")}#le(e,t,r,n,i,o){this.#M(e);let a=()=>{this.#M(e),this.#E(r,n,i,e,o)},s=setTimeout(a,t);e?.addEventListener?.("blur",a,{once:!0}),this.#S.set(e,{timer:s,flush:a})}#M(e){let t=this.#S.get(e);if(!t)return;clearTimeout(t.timer),e?.removeEventListener?.("blur",t.flush),this.#S.delete(e)}#de(){for(let e of[...this.#S.keys()])this.#M(e)}#fe(e,t,r,n,i,o){let a=this.#l.get(e)??new Map;if(a.has(r))return;let s=setTimeout(()=>{if(a.delete(r),a.size===0)this.#l.delete(e)},t);return a.set(r,s),this.#l.set(e,a),this.#E(r,n,i,e,o)}#he(){for(let e of this.#l.values())for(let t of e.values())clearTimeout(t);this.#l.clear()}#s(e,t,{cancelable:r=!1}={}){let n=new CustomEvent(e,{bubbles:!0,composed:!0,cancelable:r,detail:t});return(this.element.isConnected?this.element:document).dispatchEvent(n),n}#o(e,t,r,n){let i=()=>{if(!this.element.isConnected){console.warn("[phlex-reactive] retry() ignored — the reactive root left the DOM");return}return this.#E(e,t)};this.#s("reactive:error",{action:e,params:r,...n,retry:i})}#n(e){if(this.element?.isConnected===!1)return;this.element?.setAttribute?.("data-reactive-error",e)}#me(){this.element?.removeAttribute?.("data-reactive-error")}#pe(){let e=document.querySelector("[data-reactive-error-flash]");if(!e?.content)return;let t=e.getAttribute("data-reactive-error-flash")||"flash",r=document.getElementById(t);if(!r)return;r.appendChild(e.content.cloneNode(!0))}#ge(){{let e=m.get("devtools");if(e)return e.delay();if(le())return E("devtools").then((t)=>t.delay(),()=>{})}}#C(){return this.element?.getAttribute?.("data-reactive-debug")==="true"}#V(){return typeof performance<"u"&&typeof performance.now==="function"?performance.now():Date.now()}async#be(e,t,r,n){let{fields:i,files:o}=this.#Y(),a=this.#X(t),s={...i,...a},c=this.#a,l=o.length>0,h=l?this.#Se(c,e,s,o):JSON.stringify({token:c,act:e,params:s}),f=this.#C()?{action:e,paramNames:Object.keys(a),fieldNames:Object.keys(i),encoding:l?"multipart":"json",status:null,streams:[],tokenRefreshed:!1,started:this.#V()}:null;await this.#ge();try{if(navigator.onLine===!1){this.#c(r.inverse),this.#n("offline"),this.#o(e,t,s,{kind:"offline"});return}let d;try{let p={Accept:"text/vnd.turbo-stream.html","X-CSRF-Token":this.#Pe()};if(!l)p["Content-Type"]="application/json";let T=this.#Ie();if(T)p["X-Pgbus-Connection"]=T;ge("action"),d=await fetch(this.#xe(),{method:"POST",headers:p,body:h,credentials:"same-origin",signal:AbortSignal.timeout(this.#Re())})}catch(p){if(console.error("[phlex-reactive] action error",p),this.#c(r.inverse),p?.name==="TimeoutError"||p?.name==="AbortError"){this.#n("timeout"),this.#o(e,t,s,{kind:"timeout"});return}this.#pe(),this.#n("network"),this.#o(e,t,s,{kind:"network"});return}if(f)f.status=d.status;if(d.redirected){console.error("[phlex-reactive] action was redirected (auth/CSRF?) — no update applied"),this.#c(r.inverse),this.#n("redirected"),this.#o(e,t,s,{kind:"redirected",status:d.status});return}if(!d.ok){let p=await d.text();if(console.error(`[phlex-reactive] action failed: HTTP ${d.status}`,p),this.#c(r.inverse),(d.headers.get("Content-Type")||"").includes("turbo-stream")){let T=this.#G(p);if(this.#a=T??this.#a,f)this.#v((ke)=>ke.recordBody(f,p,T));window.Turbo.renderStreamMessage(p)}this.#n("http"),this.#o(e,t,s,{kind:"http",status:d.status,body:p});return}let g=d.headers.get("Content-Type")||"";if(!g.includes("turbo-stream")){console.error(`[phlex-reactive] expected a turbo-stream, got "${g}" — no update applied`),this.#c(r.inverse),this.#n("content-type"),this.#o(e,t,s,{kind:"content-type",status:d.status});return}let b=await d.text(),A=this.#G(b);if(this.#a=A??this.#a,f)this.#v((p)=>p.recordBody(f,b,A));if(window.Turbo.renderStreamMessage(b),r.resurrect)queueMicrotask(r.resurrect);return this.#me(),this.#s("reactive:applied",{action:e,params:s,html:b}),b}catch(d){console.error("[phlex-reactive] action error",d),this.#c(r.inverse),this.#s("reactive:error",{action:e,params:s,kind:"apply"})}finally{if(n?.(),f)this.#v((d)=>d.trace(this,{...f,ms:this.#V()-f.started}))}}get#a(){return this.#_??this.tokenValue}set#a(e){this.#_=e}tokenValueChanged(){this.#_=void 0}#G(e){let t=this.element.id;if(!t)return e.match(/data-reactive-token-value="([^"]+)"/)?.[1];let{token:r,self:n}=this.#ye(t),i=e.match(r);if(i)return i[1];let o=e.match(n);if(o)return o[1].match(/data-reactive-token-value="([^"]+)"/)?.[1];return}#ye(e){let t=this.#j;if(t&&t.id===e)return t;let r=Be(e);return this.#j={id:e,token:new RegExp(`<turbo-stream\\b[^>]*\\baction="reactive:token"[^>]*\\btarget="${r}"[^>]*\\bdata-reactive-token-value="([^"]+)"`),self:new RegExp(`<turbo-stream\\b[^>]*\\baction="(?:replace|update)"[^>]*\\btarget="${r}"[^>]*>([\\s\\S]*?)</turbo-stream>`)}}#F(e){return e.closest('[data-controller~="reactive"]')===this.element}#D(){if(this.element.querySelectorAll('[data-controller~="reactive"]').length===0)return()=>!0;return(t)=>this.#F(t)}#Y(){let e={},t=[],r=this.#D(),n=[];this.element.querySelectorAll("input[name], select[name], textarea[name]").forEach((s)=>{if(!r(s))return;if(s.type==="file")for(let c of s.files??[])t.push({name:s.name,file:c,multiple:s.multiple});else n.push(s)});let i=[];this.element.querySelectorAll(`[name]${ze}`).forEach((s)=>{if(r(s))i.push(s)});let o=this.#ve(n),a=this.#_e(n);for(let s of n)if(o.has(s.name)){let c=e[s.name]??(e[s.name]=[]);if(s.type==="checkbox"||s.type==="radio"){if(s.checked)c.push(s.value)}else if(s.type==="hidden"){if(!a.has(s.name))c.push(s.value)}else if(s.multiple&&s.options){for(let l of s.options)if(l.selected)c.push(l.value)}else c.push(s.value)}else if(s.type==="checkbox")e[s.name]=s.checked;else if(s.type==="radio"){if(s.checked)e[s.name]=s.value}else e[s.name]=s.value;return i.forEach((s)=>{let c=s.getAttribute("name");if(!c)return;let l=s.getAttribute("data-reactive-editable")!=null?F(s):s.value??s.textContent??s.innerHTML??"",h=e[c];if(String(c).endsWith("[]")&&!Ve(s))return;if(String(c).endsWith("[]")&&(h===void 0||Array.isArray(h))){(Array.isArray(h)?h:e[c]=[]).push(l);return}if(h==null||h==="")e[c]=l}),{fields:e,files:t}}#ve(e){let t=new Set;for(let r of e){if(r.type==="radio")continue;if(String(r.name).endsWith("[]"))t.add(r.name)}return t}#_e(e){let t=new Set;for(let r of e)if(r.type==="checkbox")t.add(r.name);return t}#Se(e,t,r,n){let i=new FormData;i.append("token",e),i.append("act",t);let o=[];for(let[s,c]of Object.entries(r))if(Array.isArray(c)&&String(s).endsWith("[]"))if(c.length===0)o.push(String(s).slice(0,-2));else{let l=`${this.#k(s)}[]`;for(let h of c)i.append(l,String(h))}else this.#q(i,this.#k(s),c);for(let s of o)i.append("empty_groups[]",s);let a=this.#we(n);for(let{name:s,file:c,multiple:l}of n){let f=l||a.has(s)?`${this.#k(s)}[]`:this.#k(s);i.append(f,c,c.name)}return i}#k(e){let t=String(e),r=t.indexOf("[");if(r===-1)return`params[${t}]`;return`params${[t.slice(0,r),...t.slice(r).match(/[^\[\]]+/g)??[]].map((i)=>`[${i}]`).join("")}`}#q(e,t,r){if(r==null)e.append(t,"");else if(Array.isArray(r))r.forEach((n,i)=>this.#q(e,`${t}[${i}]`,n));else if(typeof r==="object")for(let[n,i]of Object.entries(r))this.#q(e,`${t}[${n}]`,i);else e.append(t,String(r))}#we(e){let t=new Map;for(let{name:r}of e)t.set(r,(t.get(r)??0)+1);return new Set([...t].filter(([,r])=>r>1).map(([r])=>r))}#X(e){if(!e)return{};try{return typeof e==="string"?JSON.parse(e):e}catch{return{}}}#Ae(e){return K(e)}#Te(e){j(e,(t)=>this.#W(t),(t,r)=>this.#B(`client op "${t}"`,r))}#$(){return this.element?.getAttribute?.("data-reactive-verbose")==="true"||this.#C()}#B(e,t){if(!this.#$())return;this.#v((r)=>r.diagnose(this,e,t))}#v(e){y("devtools",e)}#W(e){let t=e.to;if(t==="@root")return[this.element];if(typeof t!=="string"||t==="")return[];if(e.global)return[...document.querySelectorAll(t)];return[...this.element.querySelectorAll(t)].filter((r)=>this.#F(r))}#Ee(e,t){if(e?.checked!=="keep")return!1;let r=t?.type;return r==="checkbox"||r==="radio"}#c(e){if(!e)return;if(!this.element.isConnected)return;for(let t of e)t()}#Fe(e,t,r){this.#ke(e,t),fe();let n=!1;return()=>{if(n)return;n=!0,this.#Le(e,t),he();for(let i of r.undo)i()}}#ke(e,t){if(this.#u(t,e,1),this.#u(this.element,e,1),this.#d.set(e,(this.#d.get(e)??0)+1),this.#x++===0)this.element.setAttribute("aria-busy","true");for(let r of this.#Z(e))this.#u(r,e,1)}#Le(e,t){this.#u(t,e,-1),this.#u(this.element,e,-1);let r=(this.#d.get(e)??1)-1;if(r<=0)this.#d.delete(e);else this.#d.set(e,r);if(--this.#x<=0)this.#x=0,this.element.removeAttribute("aria-busy");for(let n of this.#Z(e))this.#u(n,e,-1)}#u(e,t,r){if(!e||typeof e.getAttribute!=="function")return;let n=this.#R.get(e)??new Map,i=(n.get(t)??0)+r;if(i<=0)n.delete(t);else n.set(t,i);if(n.size===0){this.#R.delete(e),e.removeAttribute("data-reactive-busy");return}this.#R.set(e,n),e.setAttribute("data-reactive-busy",[...n.keys()].join(" "))}#Z(e){return[...this.element.querySelectorAll?.("[data-reactive-busy-on]")??[]].filter((r)=>r.getAttribute("data-reactive-busy-on")===e&&this.#F(r))}#xe(){return this.#Q??=document.querySelector('meta[name="phlex-reactive-action-path"]')?.content||"/reactive/actions"}#Re(){if(this.#L!=null)return this.#L;let e=document.querySelector('meta[name="phlex-reactive-timeout"]')?.content,t=Number(e);return this.#L=Number.isFinite(t)&&t>0?t:30000}#Pe(){return document.querySelector('meta[name="csrf-token"]')?.content??""}#Ie(){return document.querySelector("pgbus-stream-source[connection-id]")?.getAttribute("connection-id")||document.querySelector('meta[name="pgbus-connection-id"]')?.content||null}}if(typeof window<"u")if(window.Turbo)X();else document.addEventListener("turbo:load",X,{once:!0});v("persist",()=>import("phlex/reactive/features/persist"),wt);v("defer",()=>import("phlex/reactive/features/defer"));v("form",()=>import("phlex/reactive/features/form"));v("bindings",()=>import("phlex/reactive/features/bindings"));v("compute",()=>import("phlex/reactive/features/compute"),At);v("sortable",()=>import("phlex/reactive/features/sortable"));v("effects",()=>import("phlex/reactive/features/effects"));v("hints",()=>import("phlex/reactive/features/hints"));v("devtools",()=>import("phlex/reactive/features/devtools"));U();function wt(e,t){let r=()=>{t.edited=!0},n=(o)=>{let a=o.target;if(o.detail?.success&&a?.tagName==="FORM"&&a.contains?.(e))t.submitted=!0},i=["input","change","lexxy:change","trix-change"];for(let o of i)e.addEventListener?.(o,r);return document.addEventListener("turbo:submit-end",n),()=>{for(let o of i)e.removeEventListener?.(o,r);document.removeEventListener("turbo:submit-end",n)}}function At(e,t){let r=()=>{t.edited=!0};return e.addEventListener?.("input",r),e.addEventListener?.("change",r),()=>{e.removeEventListener?.("input",r),e.removeEventListener?.("change",r)}}var Tt=1000,w=null,Fe,N=new Set;function Et(e){return Ae(e).then((t)=>{if(e!=="effects")return;Fe=t,t?.sweep()})}ue((e,t)=>{let r=e.detail,n=r?.render;if(typeof n!=="function")return;let i=r.newStream??e.target,o=we(Ft(i)).filter((s)=>!N.has(s));if(!t&&!N.has("effects")&&kt(i))o.push("effects");if(o.length===0&&!w)return t?.wrap(e);if(o.length>0){for(let c of o)N.add(c);let s=w=Promise.race([Promise.all([w,...o.map(Et)]),new Promise((c)=>setTimeout(c,Tt))]).then(()=>{if(w!==s)return;w=null,N.clear()})}let a=w;r.render=async(s)=>{await a;let c={target:e.target,detail:{newStream:r.newStream,render:n}};(t??Fe)?.wrap(c),await c.detail.render(s)}});function Ft(e){let t=e?.querySelector?.("template")?.content;if(!t)return[];let r=[...t.querySelectorAll?.('[data-controller~="reactive"]')??[]];for(let n of t.children??[])if(n.matches?.('[data-controller~="reactive"]')&&!r.includes(n))r.unshift(n);if(t.querySelector?.("[data-reactive-editable]"))r.push(t);return r}function kt(e){return Boolean(e?.hasAttribute?.("data-reactive-effect")||document.getElementById?.(e?.getAttribute?.("target"))?.matches?.("[data-reactive-effect-update],[data-reactive-effect-exit]")||e?.querySelector?.("template")?.content?.querySelector?.("[data-reactive-effect-enter],[data-reactive-dismiss-after]")||document.querySelector?.("[data-reactive-dismiss-after]:not([data-reactive-dismiss-scheduled])"))}var Bt=Ee;export{de as ACTIVE_ATTR,ze as EDITOR_SELECTOR,He as EDITOR_TAGS,pe as REQUESTS_ATTR,We as checkReactiveRegistration,ge as countReactiveRequest,Bt as default,F as editableValue,fe as enterReactiveActivity,Be as escapeRegExp,he as exitReactiveActivity,Ae as loadReactiveFeature,ue as onReactiveStreamWithoutEffects,Pt as reactiveActivityCount,Ot as reactiveFeatureNames,X as registerReactiveActions,Ie as registerReactiveDefer,U as registerReactiveDev,Ne as registerReactiveDismiss,Ne as registerReactiveEffects,Nt as registerReactiveFeature,v as registerReactiveFeatureLoader,Pe as registerReactiveJs,Oe as registerReactiveMorphFocus,Ce as registerReactiveOffline,Ne as registerReactiveStreamRender,Re as registerReactiveToken,xe as registerReactiveVisit,It as resetReactiveActivity,ve as serverText,we as unloadedFeaturesFor};
|
|
2
6
|
|
|
3
|
-
//# debugId=
|
|
7
|
+
//# debugId=4F52B06C4D4D29E964756E2164756E21
|
|
4
8
|
//# sourceMappingURL=core.min.js.map
|