phlex-reactive 0.12.2 → 0.12.3
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 +49 -0
- data/README.md +135 -1
- data/app/javascript/phlex/reactive/compute.js +152 -0
- data/app/javascript/phlex/reactive/compute.min.js +2 -2
- data/app/javascript/phlex/reactive/compute.min.js.map +3 -3
- data/app/javascript/phlex/reactive/reactive_controller.js +214 -2
- data/app/javascript/phlex/reactive/reactive_controller.min.js +2 -2
- data/app/javascript/phlex/reactive/reactive_controller.min.js.map +3 -3
- data/lib/phlex/reactive/component/dsl.rb +84 -0
- data/lib/phlex/reactive/component/helpers.rb +16 -0
- data/lib/phlex/reactive/component/registry.rb +2 -0
- data/lib/phlex/reactive/component.rb +9 -0
- data/lib/phlex/reactive/js.rb +15 -0
- data/lib/phlex/reactive/show_conditions.rb +116 -0
- data/lib/phlex/reactive/streamable.rb +13 -8
- data/lib/phlex/reactive/version.rb +1 -1
- metadata +1 -1
|
@@ -1092,8 +1092,25 @@ const CLIENT_OPS = Object.freeze({
|
|
|
1092
1092
|
dispatch: (el, args) => {
|
|
1093
1093
|
el.dispatchEvent(new CustomEvent(args.name, { bubbles: true, composed: true, detail: args.detail ?? {} }))
|
|
1094
1094
|
},
|
|
1095
|
+
|
|
1096
|
+
// Submit the target's OWN form (issue #226) via requestSubmit() — constraint
|
|
1097
|
+
// validation runs and a REAL cancelable `submit` event fires, so an
|
|
1098
|
+
// on(:action, event: "submit") interception or a native/Turbo form handles it
|
|
1099
|
+
// exactly like a user submit. No form → no-op. ACTOR-ONLY like focus: the
|
|
1100
|
+
// broadcast builder refuses it server-side (BROADCAST_REFUSED_OPS).
|
|
1101
|
+
submit: (el) => submitFormFor(el)?.requestSubmit?.(),
|
|
1095
1102
|
})
|
|
1096
1103
|
|
|
1104
|
+
// The form a submit op commits (issue #226), in order: the target itself when
|
|
1105
|
+
// it IS a form (tagName, not instanceof — fake-node/test friendly), its form
|
|
1106
|
+
// owner for a control (input.form — honors a form= attribute), else the nearest
|
|
1107
|
+
// ancestor form. closest() may cross the component root by design — the
|
|
1108
|
+
// FIELD'S OWN form is the submit scope, not the reactive boundary.
|
|
1109
|
+
function submitFormFor(el) {
|
|
1110
|
+
if (el?.tagName === "FORM") return el
|
|
1111
|
+
return el?.form ?? el?.closest?.("form") ?? null
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1097
1114
|
// Apply a hidden-flag change, optionally animated by a [during, from, to]
|
|
1098
1115
|
// transition (issue #96). Split out so show/hide/toggle share it.
|
|
1099
1116
|
function setHidden(el, hidden, args) {
|
|
@@ -1165,6 +1182,39 @@ function showBindingMatches(el, value) {
|
|
|
1165
1182
|
// present. Each coerces BOTH sides to Number and compares.
|
|
1166
1183
|
const SHOW_NUMERIC_KEYS = ["gte", "gt", "lte", "lt"]
|
|
1167
1184
|
|
|
1185
|
+
// The length predicate keys (issue #226) — the client half of Ruby's
|
|
1186
|
+
// ShowConditions::LENGTH_KEYS. Length is counted in CODEPOINTS
|
|
1187
|
+
// ([...str].length), NOT UTF-16 code units (str.length), so Ruby's
|
|
1188
|
+
// String#length and this evaluator agree on multibyte values — the shared
|
|
1189
|
+
// fixture's emoji vector proves it.
|
|
1190
|
+
const SHOW_LENGTH_KEYS = ["len_eq", "len_gte", "len_gt", "len_lte", "len_lt"]
|
|
1191
|
+
|
|
1192
|
+
// Evaluate one length predicate. Length is a TOTAL function (blank/absent →
|
|
1193
|
+
// 0), so every field value is decidable — no fail-closed special case like the
|
|
1194
|
+
// numeric thresholds ({ length: 0 } legitimately matches a blank field). A
|
|
1195
|
+
// non-Integer LITERAL is a malformed binding — warn-skip (null), default-deny.
|
|
1196
|
+
function lengthPredicateMatches(key, literal, value) {
|
|
1197
|
+
if (!Number.isInteger(literal)) {
|
|
1198
|
+
console.warn(`[phlex-reactive] reactive_show ${key}: needs an integer literal, got ${JSON.stringify(literal)} — skipped`)
|
|
1199
|
+
return null
|
|
1200
|
+
}
|
|
1201
|
+
const length = [...String(value ?? "")].length
|
|
1202
|
+
switch (key) {
|
|
1203
|
+
case "len_eq":
|
|
1204
|
+
return length === literal
|
|
1205
|
+
case "len_gte":
|
|
1206
|
+
return length >= literal
|
|
1207
|
+
case "len_gt":
|
|
1208
|
+
return length > literal
|
|
1209
|
+
case "len_lte":
|
|
1210
|
+
return length <= literal
|
|
1211
|
+
case "len_lt":
|
|
1212
|
+
return length < literal
|
|
1213
|
+
default:
|
|
1214
|
+
return null
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1168
1218
|
// Evaluate one numeric threshold predicate against a field value. Returns
|
|
1169
1219
|
// true/false for a decidable comparison, or null when the LITERAL itself is
|
|
1170
1220
|
// non-numeric (a malformed binding — warn-skip, default-deny). A non-numeric
|
|
@@ -1218,6 +1268,10 @@ function showPredicateMatches(pred, value) {
|
|
|
1218
1268
|
for (const key of SHOW_NUMERIC_KEYS) {
|
|
1219
1269
|
if (key in pred) return numericPredicateMatches(key, pred[key], value)
|
|
1220
1270
|
}
|
|
1271
|
+
// Length predicates (issue #226): codepoint count vs an Integer literal.
|
|
1272
|
+
for (const key of SHOW_LENGTH_KEYS) {
|
|
1273
|
+
if (key in pred) return lengthPredicateMatches(key, pred[key], value)
|
|
1274
|
+
}
|
|
1221
1275
|
return null
|
|
1222
1276
|
}
|
|
1223
1277
|
|
|
@@ -1242,6 +1296,27 @@ function parseShowCompound(raw) {
|
|
|
1242
1296
|
return null
|
|
1243
1297
|
}
|
|
1244
1298
|
|
|
1299
|
+
// Parse a data-reactive-on-complete payload (issue #226): a JSON array of
|
|
1300
|
+
// { any: [[term, …], …], ops: [[op, args], …] } bindings. Malformed JSON, a
|
|
1301
|
+
// non-array, or a binding missing either half degrades to [] WITH a warn — a
|
|
1302
|
+
// bad payload must never throw or fire an op (client-side default-deny, the
|
|
1303
|
+
// parseShowCompound posture).
|
|
1304
|
+
function parseOnComplete(raw) {
|
|
1305
|
+
try {
|
|
1306
|
+
const list = JSON.parse(raw)
|
|
1307
|
+
if (
|
|
1308
|
+
Array.isArray(list) &&
|
|
1309
|
+
list.every((b) => b && typeof b === "object" && Array.isArray(b.any) && Array.isArray(b.ops))
|
|
1310
|
+
) {
|
|
1311
|
+
return list
|
|
1312
|
+
}
|
|
1313
|
+
} catch {
|
|
1314
|
+
// fall through to the warn
|
|
1315
|
+
}
|
|
1316
|
+
console.warn(`[phlex-reactive] malformed reactive_on_complete payload ${JSON.stringify(raw)} — skipped`)
|
|
1317
|
+
return []
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1245
1320
|
// Evaluate one DNF TERM against a resolved field value (issue #180). A missing
|
|
1246
1321
|
// field (null) or a malformed/unknown predicate folds to FALSE — fail-closed
|
|
1247
1322
|
// (default-deny): a broken AND term can't pass, a broken OR term can't reveal.
|
|
@@ -1348,6 +1423,20 @@ function parseOps(raw) {
|
|
|
1348
1423
|
}
|
|
1349
1424
|
}
|
|
1350
1425
|
|
|
1426
|
+
// Normalize a reducer's reserved $ops output (issue #226): the compute `ops`
|
|
1427
|
+
// builder (its .ops list), a raw [[name, args], ...] array, or null/undefined
|
|
1428
|
+
// (no effect this pass). An EMPTY list is the same as null — "nothing to run"
|
|
1429
|
+
// must not latch the rising edge. Anything else warns + is dropped
|
|
1430
|
+
// (default-deny, like every other malformed op source). Reducers are trusted
|
|
1431
|
+
// app code, but their ops still run through the frozen CLIENT_OPS whitelist.
|
|
1432
|
+
function computeOpsList(raw) {
|
|
1433
|
+
if (raw == null) return null
|
|
1434
|
+
const list = Array.isArray(raw) ? raw : raw.ops
|
|
1435
|
+
if (Array.isArray(list)) return list.length > 0 ? list : null
|
|
1436
|
+
console.warn("[phlex-reactive] $ops must be an ops chain or a [[op, args], ...] list — skipped")
|
|
1437
|
+
return null
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1351
1440
|
// Interpret a [[name, args], ...] op list against the frozen CLIENT_OPS
|
|
1352
1441
|
// whitelist (issues #95/#96/#97). `resolveTargets(args)` returns the element(s)
|
|
1353
1442
|
// an op applies to — the controller scopes to its root (excluding nested
|
|
@@ -1416,6 +1505,13 @@ export default class extends Controller {
|
|
|
1416
1505
|
// (single-pass write set). Per-instance, so another root's events are never
|
|
1417
1506
|
// swallowed. WeakSet: entries drop when the short-lived Event is GC'd.
|
|
1418
1507
|
#computeSelfDispatched = new WeakSet()
|
|
1508
|
+
// Issue #226: the serialized $ops chain of the LAST reducer pass (null when
|
|
1509
|
+
// absent) — the rising-edge latch, keyed on CONTENT: an identical chain
|
|
1510
|
+
// never re-fires (a capped extra keystroke can't re-submit), while a chain
|
|
1511
|
+
// that CHANGED fires again (a multi-box reducer advancing focus box-by-box
|
|
1512
|
+
// emits a different focus target per digit). The seed pass arms this
|
|
1513
|
+
// without firing.
|
|
1514
|
+
#computeOpsSignature = null
|
|
1419
1515
|
// Dirty tracking (issue #103): the bound re-scan (turbo:morph-element) and the
|
|
1420
1516
|
// navigate-away guard handlers, held so disconnect() can remove exactly them.
|
|
1421
1517
|
#boundScanDirty
|
|
@@ -1424,6 +1520,15 @@ export default class extends Controller {
|
|
|
1424
1520
|
// Show bindings (issue #161): the ONE delegated sync handler shared by the
|
|
1425
1521
|
// root's input/change/turbo:morph-element listeners, held for teardown.
|
|
1426
1522
|
#boundSyncShow
|
|
1523
|
+
// Completion bindings (issue #226): the delegated gesture handler + the
|
|
1524
|
+
// no-gesture morph arm, held for teardown; the per-binding rising-edge
|
|
1525
|
+
// latches; and the raw-attr memo that re-parses (and resets the latches)
|
|
1526
|
+
// when a morph rewrites the payload.
|
|
1527
|
+
#boundSyncOnComplete
|
|
1528
|
+
#boundArmOnComplete
|
|
1529
|
+
#onCompleteRaw
|
|
1530
|
+
#onCompleteParsed
|
|
1531
|
+
#onCompleteStates
|
|
1427
1532
|
// Option filtering (issue #163): the ONE delegated sync handler shared by the
|
|
1428
1533
|
// root's input/turbo:morph-element listeners, held for teardown.
|
|
1429
1534
|
#boundSyncFilter
|
|
@@ -1532,6 +1637,26 @@ export default class extends Controller {
|
|
|
1532
1637
|
this.#syncShow()
|
|
1533
1638
|
}
|
|
1534
1639
|
|
|
1640
|
+
// Completion bindings (issue #226) — ONLY when the root declares
|
|
1641
|
+
// data-reactive-on-complete (the show/filter gate precedent). ONE
|
|
1642
|
+
// delegated input+change listener evaluates every binding's DNF over the
|
|
1643
|
+
// owned fields and runs its ops on the RISING EDGE — the event-driven
|
|
1644
|
+
// flip to true. The connect pass ARMS without firing (a fresh render with
|
|
1645
|
+
// already-satisfied conditions must never self-fire — the $ops seed
|
|
1646
|
+
// precedent), and turbo:morph-element re-arms the same way after an
|
|
1647
|
+
// in-place morph. Listeners added HERE run after the Stimulus-wired
|
|
1648
|
+
// recompute delegation for the same event, so the evaluation reads
|
|
1649
|
+
// compute-NORMALIZED values (and a compute output write dispatches a real
|
|
1650
|
+
// input event that re-evaluates anyway).
|
|
1651
|
+
if (this.#onCompleteEnabled()) {
|
|
1652
|
+
this.#boundSyncOnComplete = (event) => this.#syncOnComplete(event)
|
|
1653
|
+
this.#boundArmOnComplete = () => this.#syncOnComplete(null)
|
|
1654
|
+
this.element.addEventListener?.("input", this.#boundSyncOnComplete)
|
|
1655
|
+
this.element.addEventListener?.("change", this.#boundSyncOnComplete)
|
|
1656
|
+
this.element.addEventListener?.("turbo:morph-element", this.#boundArmOnComplete)
|
|
1657
|
+
this.#syncOnComplete(null)
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1535
1660
|
// Option filtering (issue #163) — ONLY when the root declares the binding
|
|
1536
1661
|
// (reactive_filter emits both attrs together), so a component without one
|
|
1537
1662
|
// pays two attribute reads. ONE delegated input listener on the root — the
|
|
@@ -1635,6 +1760,7 @@ export default class extends Controller {
|
|
|
1635
1760
|
this.#clearAllThrottles()
|
|
1636
1761
|
this.#teardownDirtyTracking()
|
|
1637
1762
|
this.#teardownShowSync()
|
|
1763
|
+
this.#teardownOnCompleteSync()
|
|
1638
1764
|
this.#teardownFilterSync()
|
|
1639
1765
|
this.#teardownTagsSync()
|
|
1640
1766
|
this.#teardownNestedJsonSync()
|
|
@@ -1928,7 +2054,12 @@ export default class extends Controller {
|
|
|
1928
2054
|
// snapshot above (issue #183): its result drives the whole single-pass write.
|
|
1929
2055
|
const result = reduce(values, { changed: this.#changedComputeField(event, inputs, scope) }) || {}
|
|
1930
2056
|
|
|
1931
|
-
//
|
|
2057
|
+
// The reserved $ops output (issue #226) is CONSUMED here — normalized once,
|
|
2058
|
+
// then excluded from every write phase below (it is an op chain, never a
|
|
2059
|
+
// field value, text sink, or mirror), and applied as phase 4.
|
|
2060
|
+
const reducerOps = computeOpsList(result.$ops)
|
|
2061
|
+
|
|
2062
|
+
// Issue #183 — SINGLE-PASS WRITE SET. Ordered phases, so declared output
|
|
1932
2063
|
// order stops being semantics and a wrong order can no longer corrupt values:
|
|
1933
2064
|
//
|
|
1934
2065
|
// 1. BATCH the field writes from the ONE result. Each output name in the
|
|
@@ -1942,9 +2073,12 @@ export default class extends Controller {
|
|
|
1942
2073
|
// per-instance WeakSet) makes recompute skip re-running the reducer for our
|
|
1943
2074
|
// own write, while the event still fires every OTHER listener (chained
|
|
1944
2075
|
// repaint, dirty tracking, show bindings, sibling roots).
|
|
2076
|
+
// 4. RUN the reducer's $ops chain (issue #226) — rising-edge, event-gated —
|
|
2077
|
+
// so its ops (dispatch a completion event, submit the form) always see
|
|
2078
|
+
// the fully settled DOM and every chained listener has already run.
|
|
1945
2079
|
const changedFields = []
|
|
1946
2080
|
for (const name of outputs) {
|
|
1947
|
-
if (!(name in result)) continue
|
|
2081
|
+
if (name === "$ops" || !(name in result)) continue
|
|
1948
2082
|
const field = ownedField(name)
|
|
1949
2083
|
if (!field) continue // a non-field output paints as a text sink in phase 2
|
|
1950
2084
|
if (String(result[name]) === field.value) continue // change-guard — unchanged, skip
|
|
@@ -1958,6 +2092,7 @@ export default class extends Controller {
|
|
|
1958
2092
|
// undefined result value is SKIPPED (never stringified to "null"/"undefined") —
|
|
1959
2093
|
// the same "no value this pass, don't paint" filter #applyComputeMirrors uses.
|
|
1960
2094
|
for (const name of Object.keys(result)) {
|
|
2095
|
+
if (name === "$ops") continue
|
|
1961
2096
|
const value = result[name]
|
|
1962
2097
|
if (value === undefined || value === null) continue
|
|
1963
2098
|
this.#mirrorText(name, value)
|
|
@@ -1979,6 +2114,29 @@ export default class extends Controller {
|
|
|
1979
2114
|
this.#computeSelfDispatched.add(inputEvent)
|
|
1980
2115
|
field.dispatchEvent(inputEvent)
|
|
1981
2116
|
}
|
|
2117
|
+
|
|
2118
|
+
// Phase 4 — the reducer's $ops chain (issue #226), after everything settled.
|
|
2119
|
+
// Event-gated: a seed/direct recompute() pass (no event) arms the latch but
|
|
2120
|
+
// never fires, so a restored/re-rendered complete value can't auto-fire.
|
|
2121
|
+
this.#applyComputeOps(reducerOps, Boolean(event))
|
|
2122
|
+
}
|
|
2123
|
+
|
|
2124
|
+
// Apply a reducer-emitted $ops chain (issue #226) on its RISING EDGE, keyed
|
|
2125
|
+
// on CONTENT: fire only when THIS pass's chain differs from the LAST pass's
|
|
2126
|
+
// (including from "absent"), and only on an event-driven pass. An unchanged
|
|
2127
|
+
// chain never re-fires — "still complete, same submit" is settled, exactly
|
|
2128
|
+
// like the change-guarded field writes. A pass with no $ops re-arms; a pass
|
|
2129
|
+
// whose chain CHANGED fires again (per-keystroke focus advance across OTP
|
|
2130
|
+
// boxes is a different focus target each time — a deliberately new intent).
|
|
2131
|
+
// Ops run through the shared CLIENT_OPS interpreter with runOps's
|
|
2132
|
+
// root-scoped target resolution; a missing to: defaults to "@root"
|
|
2133
|
+
// (hand-written reducer ergonomics).
|
|
2134
|
+
#applyComputeOps(list, eventDriven) {
|
|
2135
|
+
const signature = list === null ? null : JSON.stringify(list)
|
|
2136
|
+
const fire = signature !== null && signature !== this.#computeOpsSignature && eventDriven
|
|
2137
|
+
this.#computeOpsSignature = signature
|
|
2138
|
+
if (!fire) return
|
|
2139
|
+
applyOps(list, (args) => this.#opTargets(args.to == null ? { ...args, to: "@root" } : args))
|
|
1982
2140
|
}
|
|
1983
2141
|
|
|
1984
2142
|
// Client-side list navigation (combobox keyboard nav, issue #72). Wired by
|
|
@@ -3388,6 +3546,60 @@ export default class extends Controller {
|
|
|
3388
3546
|
return false
|
|
3389
3547
|
}
|
|
3390
3548
|
|
|
3549
|
+
// The connect() gate for completion bindings (issue #226) — one attribute read.
|
|
3550
|
+
#onCompleteEnabled() {
|
|
3551
|
+
return !!this.element.getAttribute?.("data-reactive-on-complete")
|
|
3552
|
+
}
|
|
3553
|
+
|
|
3554
|
+
// Parse-and-memoize the completion bindings, keyed on the RAW attr string:
|
|
3555
|
+
// a morph that rewrote the payload re-parses and RESETS the latches (the
|
|
3556
|
+
// morph listener's own arm pass then re-arms without firing). A removed
|
|
3557
|
+
// attr yields [] silently; malformed JSON warns (in parseOnComplete).
|
|
3558
|
+
#onCompleteBindings() {
|
|
3559
|
+
const raw = this.element.getAttribute?.("data-reactive-on-complete") ?? null
|
|
3560
|
+
if (raw !== this.#onCompleteRaw) {
|
|
3561
|
+
this.#onCompleteRaw = raw
|
|
3562
|
+
this.#onCompleteParsed = raw == null ? [] : parseOnComplete(raw)
|
|
3563
|
+
this.#onCompleteStates = this.#onCompleteParsed.map(() => false)
|
|
3564
|
+
}
|
|
3565
|
+
return this.#onCompleteParsed
|
|
3566
|
+
}
|
|
3567
|
+
|
|
3568
|
+
// Evaluate every completion binding (issue #226) over the owned fields —
|
|
3569
|
+
// the SAME memoized, scope-aware field resolver and DNF fold the show sync
|
|
3570
|
+
// uses — and run each binding's ops on ITS OWN rising edge. `event` null
|
|
3571
|
+
// (the connect/morph arm pass) updates the latches WITHOUT firing, so ops
|
|
3572
|
+
// only ever run from a real user gesture. An undecidable payload (no
|
|
3573
|
+
// groups) leaves its latch alone — default-deny, like every malformed-wire
|
|
3574
|
+
// arm. Ops resolve through runOps's root-scoped targets; a missing to:
|
|
3575
|
+
// defaults to "@root" (the $ops convention).
|
|
3576
|
+
#syncOnComplete(event) {
|
|
3577
|
+
const bindings = this.#onCompleteBindings()
|
|
3578
|
+
if (!bindings.length) return
|
|
3579
|
+
|
|
3580
|
+
const owns = this.#ownershipFilter()
|
|
3581
|
+
const scope = this.element.getAttribute?.("data-reactive-scope") || null
|
|
3582
|
+
const values = new Map()
|
|
3583
|
+
const fieldValue = (name) => {
|
|
3584
|
+
if (!values.has(name)) values.set(name, this.#showFieldValue(name, owns, scope))
|
|
3585
|
+
return values.get(name)
|
|
3586
|
+
}
|
|
3587
|
+
bindings.forEach((binding, i) => {
|
|
3588
|
+
const matches = anyOfAllsMatches(binding.any, fieldValue)
|
|
3589
|
+
if (matches === null) return
|
|
3590
|
+
const fire = matches && !this.#onCompleteStates[i] && Boolean(event)
|
|
3591
|
+
this.#onCompleteStates[i] = matches
|
|
3592
|
+
if (fire) applyOps(binding.ops, (args) => this.#opTargets(args.to == null ? { ...args, to: "@root" } : args))
|
|
3593
|
+
})
|
|
3594
|
+
}
|
|
3595
|
+
|
|
3596
|
+
#teardownOnCompleteSync() {
|
|
3597
|
+
if (!this.#boundSyncOnComplete) return
|
|
3598
|
+
this.element.removeEventListener?.("input", this.#boundSyncOnComplete)
|
|
3599
|
+
this.element.removeEventListener?.("change", this.#boundSyncOnComplete)
|
|
3600
|
+
this.element.removeEventListener?.("turbo:morph-element", this.#boundArmOnComplete)
|
|
3601
|
+
}
|
|
3602
|
+
|
|
3391
3603
|
// Re-evaluate every OWNED show binding in one pass (issue #161): read the
|
|
3392
3604
|
// controlling field's current value, evaluate the declared literal predicate,
|
|
3393
3605
|
// toggle `hidden`. A full pass (not per-target) for the same reason as
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{Controller as WX}from"@hotwired/stimulus";import{confirmResolver as R}from"phlex/reactive/confirm";import{computeReducer as JX}from"phlex/reactive/compute";import{confirmPredicate as LX}from"phlex/reactive/confirm_predicate";function VX(){let X=window.Turbo?.StreamActions;if(!X||X["reactive:visit"])return;X["reactive:visit"]=function(){let Z=this.getAttribute("data-url");if(Z)window.Turbo.visit(Z,{action:"advance"})}}function BX(){let X=window.Turbo?.StreamActions;if(!X||X["reactive:token"])return;X["reactive:token"]=function(){let Z=this.getAttribute("data-reactive-token-value"),$=this.getAttribute("target");if(!Z||!$)return;let Q=document.getElementById($);if(Q)Q.setAttribute("data-reactive-token-value",Z)}}function _X(){let X=window.Turbo?.StreamActions;if(!X||X["reactive:js"])return;X["reactive:js"]=function(){let Z=HX(this.getAttribute("data-reactive-ops"));if(!Z.length)return;let $=this.getAttribute("target"),Q=$?document.getElementById($):null;if($&&!Q)return;UX(Z,(j)=>ZZ(j,Q))}}var _=new Map;function s(X,Z){let $=!_.has(X);if(_.set(X,Z),$)QX()}function N(X){if(_.delete(X))jX()}function GZ(){_.clear(),S=!1}function qZ(X){return _.get(X)?.via}var S=!1;function AX(){let X=window.Turbo?.StreamActions;if(!X||X["reactive:defer"])return;if(X["reactive:defer"]=function(){let Z=this.getAttribute("target");if(!Z)return;if(this.getAttribute("data-reactive-defer-via")==="stream"){OX(Z,this);return}let $=this.getAttribute("data-reactive-defer-token");if(!$)return;C(Z,$)},!S&&typeof document<"u"&&document.addEventListener)S=!0,document.addEventListener("turbo:before-stream-render",MX)}function MX(X){let $=X.target?.getAttribute?.("target");if(!$)return;let Q=$.startsWith("reactive-defer-src-")?$.slice(19):$;if(_.get(Q)?.via==="stream")N(Q)}function C(X,Z){let $=document.getElementById(X);if(!$){console.warn(`[phlex-reactive] reactive:defer target #${X} is not on the page — skipped`);return}a(X),r($);let Q={via:"fetch",abort:new AbortController,timedOut:!1};s(X,Q),xX(X,Q,Z)}function OX(X,Z){let $=document.getElementById(X);if(!$){console.warn(`[phlex-reactive] reactive:defer target #${X} is not on the page — skipped`);return}let Q=Z.getAttribute("data-reactive-defer-src");if(!Q)return;if(!globalThis.customElements?.get?.("pgbus-stream-source")){let z=Z.getAttribute("data-reactive-defer-token");if(z){C(X,z);return}console.error("[phlex-reactive] reactive:defer via=stream but <pgbus-stream-source> is not registered "+"and no fallback token was provided — is the pgbus client loaded on this page?");return}a(X),r($);let j=document.createElement("pgbus-stream-source");j.id=n(X),j.setAttribute("src",Q),j.setAttribute("since-id",Z.getAttribute("data-reactive-defer-since-id")??"0"),j.setAttribute("hidden",""),document.body.appendChild(j),s(X,{via:"stream"})}function n(X){return`reactive-defer-src-${X}`}async function xX(X,Z,$){let Q=setTimeout(()=>{Z.timedOut=!0,Z.abort.abort()},CX()),j;try{j=await fetch(PX(),{method:"POST",headers:{Accept:"text/vnd.turbo-stream.html","Content-Type":"application/json","X-CSRF-Token":NX()},body:JSON.stringify({token:$}),credentials:"same-origin",signal:Z.abort.signal})}catch(G){if(clearTimeout(Q),_.get(X)!==Z)return;console.error("[phlex-reactive] deferred render failed",G),F(X,$);return}if(_.get(X)!==Z){clearTimeout(Q);return}if(j.status===204){clearTimeout(Q),g(X);return}if(!j.ok){clearTimeout(Q),console.error(`[phlex-reactive] deferred render failed: HTTP ${j.status}`),F(X,$,j.status);return}let z;try{z=await j.text()}catch(G){if(clearTimeout(Q),_.get(X)!==Z)return;console.error("[phlex-reactive] deferred render failed reading the body",G),F(X,$);return}if(clearTimeout(Q),_.get(X)!==Z)return;g(X),window.Turbo.renderStreamMessage(z)}function a(X){let Z=_.get(X);if(!Z)return;if(N(X),Z.via==="fetch")Z.abort.abort();else document.getElementById(n(X))?.remove?.()}function r(X){X.setAttribute("data-reactive-defer-pending","true"),X.setAttribute("aria-busy","true")}function t(X){X.removeAttribute("data-reactive-defer-pending"),X.removeAttribute("aria-busy")}function g(X){N(X);let Z=document.getElementById(X);if(!Z)return;t(Z),Z.removeAttribute("data-reactive-error")}function F(X,Z,$){N(X);let Q=document.getElementById(X);if(!Q)return;t(Q),Q.setAttribute("data-reactive-error","defer");let j=()=>{let z=document.getElementById(X);if(!z){console.warn("[phlex-reactive] defer retry() ignored — the target left the DOM");return}z.removeAttribute("data-reactive-error"),C(X,Z)};Q.dispatchEvent(new CustomEvent("reactive:error",{bubbles:!0,composed:!0,detail:{kind:"defer",target:X,status:$,retry:j}}))}function PX(){return document.querySelector('meta[name="phlex-reactive-defer-path"]')?.content||"/reactive/defer"}function NX(){return document.querySelector('meta[name="csrf-token"]')?.content??""}function CX(){let X=document.querySelector('meta[name="phlex-reactive-timeout"]')?.content,Z=Number(X);return Number.isFinite(Z)&&Z>0?Z:30000}var b=!1;function DX(){if(b)return;if(typeof document>"u"||!document.addEventListener)return;b=!0,document.addEventListener("turbo:before-stream-render",RX)}function RX(X){let Z=X.detail,$=Z?.render;if(typeof $!=="function"||$.__reactiveDismissWrapped){if(typeof requestAnimationFrame==="function")requestAnimationFrame(I);else setTimeout(I,0);return}let Q=async(j)=>{await $(j),I()};Q.__reactiveDismissWrapped=!0,Z.render=Q}function I(){let X=document.querySelectorAll("[data-reactive-dismiss-after]");for(let Z of X){if(Z.hasAttribute("data-reactive-dismiss-scheduled"))continue;let $=Number(Z.getAttribute("data-reactive-dismiss-after"));if(!Number.isFinite($)||$<=0)continue;Z.setAttribute("data-reactive-dismiss-scheduled",""),setTimeout(()=>Z.remove(),$)}}function YZ(){b=!1}var FX=Object.freeze({append:"enter",prepend:"enter",replace:"update",update:"update",remove:"exit"}),T=Object.freeze(["fade","slide","scale","highlight","shake"]),w="data-reactive-fx-pending",e=1000,h=!1;function IX(){if(h)return;if(typeof document>"u"||typeof document.addEventListener!=="function")return;h=!0,document.addEventListener("turbo:before-stream-render",TX)}function KZ(){h=!1}function TX(X){let Z=X.detail,$=Z?.render;if(typeof $!=="function"||$.__reactiveEffectsWrapped)return;let Q=Z?.newStream??X.target,j=FX[Q?.getAttribute?.("action")];if(!j||SX())return;let z=EX(Q,j);if(!z)return;let G=j==="exit"?async(q)=>{await hX(x(Q),z),await $(q)}:async(q)=>{let Y=j==="enter"?bX(Q):null;if(await $(q),j==="enter")wX(Y,z);else XX(x(Q),z)};G.__reactiveEffectsWrapped=!0,Z.render=G}function EX(X,Z){let $=X.getAttribute?.("data-reactive-effect");if($==="off")return null;if($)return c($,Z);let j=(Z==="enter"?kX(X):x(X))?.getAttribute?.(`data-reactive-effect-${Z}`);return j?c(j,Z):null}function x(X){let Z=X.getAttribute?.("target");return Z?document.getElementById?.(Z)??null:null}function kX(X){return X.querySelector?.("template")?.content?.firstElementChild??null}function c(X,Z){if(X.startsWith("[")){let Q=null;try{let j=JSON.parse(X);if(Array.isArray(j)&&j.length===3)Q=j.map(String)}catch{}if(Q)return{legs:Q};return console.warn(`[phlex-reactive] malformed effect legs ${JSON.stringify(X)} — skipped`),null}let $=X==="random"?T[Math.floor(Math.random()*T.length)]:X;if(!T.includes($))return console.warn(`[phlex-reactive] unknown effect ${JSON.stringify(X)} — skipped`),null;return{className:`reactive-fx--${$}-${Z}`}}function SX(){try{return typeof matchMedia==="function"&&matchMedia("(prefers-reduced-motion: reduce)").matches}catch{return!1}}function bX(X){let Z=X.querySelector?.("template")?.content;if(!Z)return null;for(let $ of Array.from(Z.children??[]))$.setAttribute?.(w,"");return x(X)}function wX(X,Z){if(typeof X?.querySelectorAll!=="function")return;for(let $ of Array.from(X.querySelectorAll(`[${w}]`)))$.removeAttribute(w),XX($,Z)}async function hX(X,Z){if(!X?.classList)return;if(Z.legs){await ZX(X,Z.legs);return}X.classList.add(Z.className);let $=p(X);if($<=0){X.classList.remove(Z.className);return}await f(X,$),X.classList.remove(Z.className)}function XX(X,Z){if(!X?.classList)return;if(Z.legs){ZX(X,Z.legs);return}if(X.classList.contains(Z.className))X.classList.remove(Z.className),X.offsetWidth;X.classList.add(Z.className);let $=p(X);if($<=0){X.classList.remove(Z.className);return}let Q=X.__reactiveFxToken=(X.__reactiveFxToken??0)+1;f(X,$).then(()=>{if(X.__reactiveFxToken===Q)X.classList.remove(Z.className)})}async function ZX(X,Z){let[$,Q,j]=Z.map(yX),z=X.__reactiveFxToken=(X.__reactiveFxToken??0)+1;if(X.classList.remove(...$,...Q,...j),X.classList.add(...$,...Q),await vX(),X.__reactiveFxToken!==z)return;X.classList.remove(...Q),X.classList.add(...j);let G=p(X);if(G>0)await f(X,G);if(X.__reactiveFxToken!==z)return;X.classList.remove(...$,...j)}function yX(X){return String(X??"").split(/\s+/).filter(Boolean)}function p(X){if(typeof getComputedStyle!=="function")return 0;try{let Z=getComputedStyle(X),$=(z)=>String(z??"").split(",").reduce((G,q)=>Math.max(G,parseFloat(q)||0),0),Q=$(Z.animationDuration)+$(Z.animationDelay),j=$(Z.transitionDuration)+$(Z.transitionDelay);return Math.min(Math.max(Q,j)*1000,e)}catch{return 0}}function f(X,Z){return new Promise(($)=>{let Q=!1,j=()=>{if(Q)return;Q=!0,$()};X.addEventListener?.("animationend",j,{once:!0}),X.addEventListener?.("transitionend",j,{once:!0}),setTimeout(j,Math.min(Z+50,e))})}function vX(){return new Promise((X)=>{if(typeof requestAnimationFrame==="function")requestAnimationFrame(()=>X());else setTimeout(X,16)})}var y=!1;function pX(){if(y)return;if(typeof window>"u"||typeof document>"u")return;if(typeof window.addEventListener!=="function")return;y=!0;let X=()=>{let Z=document.documentElement;if(typeof Z?.toggleAttribute!=="function")return;Z.toggleAttribute("data-reactive-offline",globalThis.navigator?.onLine===!1)};X(),window.addEventListener("online",X),window.addEventListener("offline",X)}function HZ(){y=!1}var m="phlex-reactive:latency",P=!1;function fX(X){if(typeof sessionStorage>"u")return;sessionStorage.setItem(m,String(X))}function mX(){if(typeof sessionStorage>"u")return;sessionStorage.removeItem(m),P=!1}function uX(){if(typeof window>"u"||typeof document>"u")return;if(document.querySelector?.('meta[name="phlex-reactive-env"]')?.content!=="development")return;window.PhlexReactive={enableLatencySim:fX,disableLatencySim:mX}}function UZ(){P=!1}var $X="data-reactive-active",A=0;function QX(){if(A++,A===1)zX("reactive:busy")}function jX(){if(A===0)return;if(A--,A===0)zX("reactive:idle")}function WZ(){return A}function JZ(){A=0,(typeof document<"u"?document.documentElement:null)?.removeAttribute?.($X)}function zX(X){if(typeof document>"u")return;let Z=document.documentElement;if(typeof Z?.toggleAttribute==="function")Z.toggleAttribute($X,A>0);if(typeof document.dispatchEvent==="function"&&typeof CustomEvent==="function")document.dispatchEvent(new CustomEvent(X,{detail:{count:A}}))}function d(){VX(),BX(),_X(),AX(),DX(),IX(),pX(),uX()}function gX(X){return X.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}if(typeof window<"u")if(window.Turbo)d();else document.addEventListener("turbo:load",d,{once:!0});var D=!1;function cX(){if(D)return;if(typeof document>"u")return;let X=document.querySelectorAll('[data-controller~="reactive"]');if(!X||X.length===0)return;console.warn("[phlex-reactive] found "+X.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.')}function LZ(){D=!1}function VZ(){D=!0}if(typeof window<"u"&&typeof document<"u"){let X=()=>setTimeout(cX,0);if(document.readyState==="loading")document.addEventListener("DOMContentLoaded",X,{once:!0});else X()}var dX=new Set(["href","src","srcdoc","action","formaction","xlink:href","style"]);function lX(X){let Z=String(X).toLowerCase();return Z.startsWith("on")||dX.has(Z)}function iX(X,Z,$){let[Q,j,z]=Z;X.classList.add(Q,j),$(),requestAnimationFrame(()=>{X.classList.remove(j),X.classList.add(z)});let G=!1,q=()=>{if(G)return;G=!0,X.classList.remove(Q,z)};X.addEventListener("animationend",q,{once:!0}),setTimeout(q,350)}var l=Object.freeze({show:(X,Z)=>E(X,!1,Z),hide:(X,Z)=>E(X,!0,Z),toggle:(X,Z)=>E(X,!X.hidden,Z),add_class:(X,Z)=>X.classList.add(...Z.classes??[]),remove_class:(X,Z)=>X.classList.remove(...Z.classes??[]),toggle_class:(X,Z)=>(Z.classes??[]).forEach(($)=>X.classList.toggle($)),set_attr:(X,Z)=>{if(k(Z.name))X.setAttribute(Z.name,Z.value??"")},remove_attr:(X,Z)=>{if(k(Z.name))X.removeAttribute(Z.name)},toggle_attr:(X,Z)=>{if(!k(Z.name))return;if(X.hasAttribute(Z.name))X.removeAttribute(Z.name);else X.setAttribute(Z.name,"")},focus:(X)=>X.focus?.(),focus_first:(X)=>XZ(X)?.focus?.(),text:(X,Z)=>{let $=String(Z.value??"");if(X.textContent!==$)X.textContent=$},dispatch:(X,Z)=>{X.dispatchEvent(new CustomEvent(Z.name,{bubbles:!0,composed:!0,detail:Z.detail??{}}))}});function E(X,Z,$){if($?.transition)iX(X,$.transition,()=>X.hidden=Z);else X.hidden=Z}function k(X){if(!lX(X))return!0;return console.warn(`[phlex-reactive] refused client attr op on ${JSON.stringify(X)} — skipped`),!1}var GX=/^#[A-Za-z_][\w-]*$/;function oX(X){if(typeof X==="string"&&GX.test(X))return!0;return console.warn(`[phlex-reactive] refused cross-root mirror target ${JSON.stringify(X)} — skipped`),!1}function sX(X,Z){let $=X.getAttribute("data-reactive-show-equals");if($!==null)return Z===$;let Q=X.getAttribute("data-reactive-show-not");if(Q!==null)return Z!==Q;let j=X.getAttribute("data-reactive-show-in");if(j!==null){try{let z=JSON.parse(j);if(Array.isArray(z))return z.includes(Z)}catch{}return console.warn(`[phlex-reactive] malformed reactive_show in: list ${JSON.stringify(j)} — skipped`),null}for(let z of qX){let G=X.getAttribute(`data-reactive-show-${z}`);if(G!==null)return YX(z,G,Z)}return console.warn("[phlex-reactive] a reactive_show binding declares no predicate — skipped"),null}var qX=["gte","gt","lte","lt"];function YX(X,Z,$){let Q=Number(Z);if(Number.isNaN(Q))return console.warn(`[phlex-reactive] reactive_show ${X}: needs a numeric literal, got ${JSON.stringify(Z)} — skipped`),null;let j=$==null?"":String($).trim(),z=j===""?NaN:Number(j);if(Number.isNaN(z))return!1;switch(X){case"gte":return z>=Q;case"gt":return z>Q;case"lte":return z<=Q;case"lt":return z<Q;default:return null}}function KX(X,Z){if(!X||typeof X!=="object")return null;if(typeof X.equals==="string")return Z===X.equals;if(typeof X.not==="string")return Z!==X.not;if(Array.isArray(X.in))return X.in.includes(Z);for(let $ of qX)if($ in X)return YX($,X[$],Z);return null}var i="[data-reactive-show-field], [data-reactive-show]";function nX(X){try{let Z=JSON.parse(X);if(Z&&typeof Z==="object"&&!Array.isArray(Z))return Z}catch{}return console.warn(`[phlex-reactive] malformed compound reactive_show payload ${JSON.stringify(X)} — skipped`),null}function u(X,Z){if(!X||typeof X!=="object"||typeof X.field!=="string")return!1;let $=Z(X.field)??"";return KX(X,$)===!0}function v(X,Z){if(!Array.isArray(X)||X.length===0)return null;return X.some(($)=>Array.isArray($)&&$.length>0&&$.every((Q)=>u(Q,Z)))}function aX(X){if(!Array.isArray(X)||X.length===0)return null;let Z=new Set;for(let $ of X){if(!Array.isArray($))continue;for(let Q of $)if(Q&&typeof Q==="object"&&typeof Q.field==="string")Z.add(Q.field)}return Z.size>0?[...Z]:null}function rX(X,Z){if(!X||typeof X!=="object")return null;let $=X.any;if(Array.isArray($)&&($.length===0||Array.isArray($[0])))return v($,Z);return tX(X,Z)}function tX(X,Z){let $=Array.isArray(X.all)?"all":Array.isArray(X.any)?"any":null;if(!$)return null;let Q=X[$];if(Q.length===0)return null;let j=Q.map((z)=>u(z,Z));return $==="all"?j.every(Boolean):j.some(Boolean)}function o(X){if(typeof X==="string"&&GX.test(X))return!0;return console.warn(`[phlex-reactive] refused cross-root show target ${JSON.stringify(X)} — skipped`),!1}var eX='a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';function XZ(X){return X.querySelectorAll?.(eX)?.[0]??null}function HX(X){if(Array.isArray(X))return X;if(typeof X!=="string")return[];try{let Z=JSON.parse(X);return Array.isArray(Z)?Z:[]}catch{return[]}}function UX(X,Z){for(let $ of X){if(!Array.isArray($))continue;let[Q,j={}]=$;if(!Object.hasOwn(l,Q)){console.warn(`[phlex-reactive] unknown client op ${JSON.stringify(Q)} — skipped`);continue}for(let z of Z(j))l[Q](z,j)}}function ZZ(X,Z){let $=X.to;if(Z){if($==="@root")return[Z];if(typeof $!=="string"||$==="")return[];if(X.global)return[...document.querySelectorAll($)];return[...Z.querySelectorAll($)]}if(typeof $!=="string"||$===""||$==="@root")return[];return[...document.querySelectorAll($)]}class BZ extends WX{static values={token:String};#m;#M=new Map;#H=new Map;#_X;#I;#u;#T=0;#U=new Map;#E=new WeakMap;#O=new Map;#g=new WeakSet;#W;#J;#L;#Z;#Q;#V;#c=!1;#k=0;#d=!1;#j;#B;#_;#x;connect(){if(D=!0,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-defer-token"))this.#l(),this.#x=()=>this.#l(),this.element.addEventListener?.("turbo:morph-element",this.#x);if(this.#AX()){if(this.#W=()=>this.#h(),this.element.addEventListener?.("turbo:morph-element",this.#W),this.#h(),this.element.getAttribute?.("data-reactive-warn-unsaved")==="true")this.#cX()}if(this.#lX())this.#Z=()=>this.#GX(),this.element.addEventListener?.("input",this.#Z),this.element.addEventListener?.("change",this.#Z),this.element.addEventListener?.("turbo:morph-element",this.#Z),this.#GX();if(this.#R())this.#Q=(X)=>{if(X?.type==="input"&&!this.#tX(X))return;this.#A()},this.element.addEventListener?.("input",this.#Q),this.element.addEventListener?.("turbo:morph-element",this.#Q),this.#A();if(this.#F())this.#V=()=>this.#p(),this.element.addEventListener?.("turbo:morph-element",this.#V),this.#p();if(this.#XZ())this.#j=(X)=>this.syncNestedJson(X),this.#B=()=>this.#N(),this.element.addEventListener?.("input",this.#j),this.element.addEventListener?.("change",this.#j),this.element.addEventListener?.("turbo:morph-element",this.#B),this.#N();if(this.#rX())this.#_=()=>this.recompute(),this.element.addEventListener?.("turbo:morph-element",this.#_),this.recompute()}#AX(){if((this.element.getAttribute?.("data-action")??"").includes("reactive#trackDirty"))return!0;let X=this.element.querySelectorAll?.('[data-action*="reactive#trackDirty"]')??[];for(let Z of X)if(this.#$(Z))return!0;return!1}disconnect(){if(this.#SX(),this.#wX(),this.#dX(),this.#aX(),this.#eX(),this.#qZ(),this.#YZ(),this.#KZ(),this.#x)this.element.removeEventListener?.("turbo:morph-element",this.#x)}#l(){let X=this.element;if(!X?.id)return;let Z=X.getAttribute?.("data-reactive-defer-token");if(!Z)return;if(X.getAttribute?.("data-reactive-defer-pending")!=="true")return;C(X.id,Z)}dispatch(X){let{action:Z,params:$,debounce:Q,throttle:j,confirm:z,confirmWhen:G,outside:q,window:Y,optimistic:K}=X.params;if(!Z)return;let W=X.params.busy??this.#MZ(X.params.loading);if(q&&this.element.contains(X.target))return;let B=X.currentTarget??X.target;if(!Y&&!this.#WZ(K,B))X.preventDefault();let J=this.#w(z,G);if(!J)return this.#e(B,Z,$,Q,j,K,W);Promise.resolve().then(()=>R(J,{el:B})).catch(()=>!1).then((L)=>{if(L)this.#e(B,Z,$,Q,j,K,W)})}runOps(X){let{ops:Z,confirm:$,confirmWhen:Q,outside:j,window:z}=X.params,G=X.currentTarget??X.target;if(j&&this.element.contains(X.target))return;if(!z)X.preventDefault();let q=this.#w($,Q);if(!q)return this.#WX(this.#UX(Z));Promise.resolve().then(()=>R(q,{el:G})).catch(()=>!1).then((Y)=>{if(Y)this.#WX(this.#UX(Z))})}trackDirty(){this.#h()}recompute(X){if(X&&this.#g.has(X))return;let Z=this.#DX(),$=Z.map(([H])=>H),Q=this.element.getAttribute?.("data-reactive-scope")||null,j=(H)=>Q&&!H.includes("[")?`${Q}[${H}]`:H,z=this.#X(),G=new Map,q=(H)=>{if(G.has(H))return G.get(H);let U=null;for(let M of this.element.querySelectorAll(`[name="${j(H)}"]`))if(z(M)){U=M;break}return G.set(H,U),U};for(let H of $)this.#r(H,q(H)?.value??"");let Y=this.element.getAttribute("data-reactive-compute-reducer-param"),K=Y?JX(Y):null;if(!K){this.#t({},q);return}let W=this.#CX("data-reactive-compute-outputs-param"),B={};for(let[H,U]of Z){let M=q(H);if(U==="string")B[H]=M?.value??"";else{let V=Number(M?.value);B[H]=Number.isFinite(V)?V:0}}let J=K(B,{changed:this.#RX(X,$,Q)})||{},L=[];for(let H of W){if(!(H in J))continue;let U=q(H);if(!U)continue;if(String(J[H])===U.value)continue;U.value=J[H],L.push(U)}for(let H of Object.keys(J)){let U=J[H];if(U===void 0||U===null)continue;this.#r(H,U)}this.#t(J,q);for(let H of L){let U=new Event("input",{bubbles:!0});this.#g.add(U),H.dispatchEvent(U)}}listnavNext(X){this.#i(X,1)}listnavPrev(X){this.#i(X,-1)}listnavPick(X){let Z=this.#P(X),$=Z.findIndex((Q)=>Q.hasAttribute("data-reactive-highlighted"));if($<0)return;X.preventDefault(),Z[$].click()}listnavClose(X){for(let Z of this.#P(X))Z.removeAttribute("data-reactive-highlighted")}#i(X,Z){let $=this.#P(X);if(!$.length)return;X.preventDefault();let Q=$.findIndex((G)=>G.hasAttribute("data-reactive-highlighted")),j=Q<0?Z>0?0:$.length-1:(Q+Z+$.length)%$.length;for(let G of $)G.removeAttribute("data-reactive-highlighted");let z=$[j];z.setAttribute("data-reactive-highlighted","true"),z.scrollIntoView?.({block:"nearest"})}#P(X){let $=(X?.currentTarget??X?.target??this.element).getAttribute?.("data-reactive-listnav-option-param")??this.element.getAttribute("data-reactive-listnav-option-param");if(!$)return[];let Q=this.#X();return Array.from(this.element.querySelectorAll($)).filter((j)=>!j.hidden&&Q(j))}tagsAdd(X){if(!this.#F())return;if(X?.defaultPrevented)return;if(this.#P(X).some((Q)=>Q.hasAttribute?.("data-reactive-highlighted")))return;X?.preventDefault?.();let Z=X?.currentTarget??X?.target;if(!Z)return;if(!this.#YX(String(Z.value??"").split(",")))return;if(Z.value="",this.#R())this.#A()}tagsPick(X){if(!this.#F())return;X?.preventDefault?.();let $=(X?.currentTarget??X?.target)?.getAttribute?.("data-reactive-tag-param");if(!$)return;if(!this.#YX([$]))return;let Q=this.#ZZ();if(!Q)return;Q.value="",this.#A(),Q.focus?.()}tagsRemove(X){if(!this.#F())return;X?.preventDefault?.();let $=(X?.currentTarget??X?.target)?.getAttribute?.("data-reactive-tag-param");if(!$)return;let Q=this.#y();if(!Q)return;let j=this.#v(Q),z=j.filter((G)=>G.toLowerCase()!==$.toLowerCase());if(z.length===j.length)return;this.#KX(Q,z)}nestedAdd(X){X?.preventDefault?.();let Z=X?.currentTarget??X?.target,$=Z?.getAttribute?.("data-reactive-association-param");if(!$)return;if(typeof this.element?.querySelectorAll!=="function")return;let Q=this.#X(),j=[...this.element.querySelectorAll(`[data-reactive-nested-list="${$}"]`)].find(Q),G=[...this.element.querySelectorAll(`[data-reactive-nested-template="${$}"]`)].find(Q)?.content?.firstElementChild;if(!j||!G){this.#GZ($);return}let q=G.cloneNode(!0);this.#zZ(q,this.#jZ()),j.appendChild(q);let Y=Z?.getAttribute?.("data-reactive-nested-from-param"),K=Z?.getAttribute?.("data-reactive-nested-clear-param")==="true",W=this.#MX(q,Y,K);if(Y)W?.focus?.();else[...q.querySelectorAll?.("input, select, textarea")??[]][0]?.focus?.();if(j.getAttribute?.("data-reactive-nested-json")===$)this.#s($)}#MX(X,Z,$){if(!Z)return null;let Q;try{Q=JSON.parse(Z)}catch{return null}if(!Q||typeof Q!=="object")return null;let j=this.#X(),z=[...X.querySelectorAll?.("input, select, textarea")??[]],G=[];for(let[q,Y]of Object.entries(Q)){let K=[...this.element.querySelectorAll?.(Y)??[]].find(j);if(!K)continue;let W=z.find((B)=>this.#a(B.getAttribute?.("name"))===q);if(!W)continue;this.#OX(W,K),G.push(K)}if($)for(let q of G)this.#xX(q);return G[0]??null}#OX(X,Z){if(X.type==="checkbox")X.checked=Z.type==="checkbox"?!!Z.checked:this.#S(Z)!=="";else X.value=this.#S(Z);if(typeof X.dispatchEvent==="function")X.dispatchEvent(new Event("input",{bubbles:!0}))}#xX(X){if(X.type==="checkbox")X.checked=!1;else X.value="";if(typeof X.dispatchEvent==="function")X.dispatchEvent(new Event("input",{bubbles:!0}))}nestedRemove(X){X?.preventDefault?.();let Z=X?.currentTarget??X?.target,$=Z?.closest?.("[data-reactive-nested-row]");if(!$)return;if($.closest?.('[data-controller~="reactive"]')!==this.element)return;let Q=Z?.getAttribute?.("data-reactive-confirm-param"),j=Z?.getAttribute?.("data-reactive-confirm-when-param"),z=this.#w(Q,j);if(!z)return this.#o($);let G=this.#n($),q=this.#PX(z,G);return Promise.resolve().then(()=>R(q,{el:Z,row:$,fields:G})).catch(()=>!1).then((Y)=>{if(Y)this.#o($)})}#PX(X,Z){if(!X.includes("%{"))return X;return X.replace(/%\{(\w+)\}/g,($,Q)=>Object.prototype.hasOwnProperty.call(Z,Q)?Z[Q]:$)}#o(X){let Z=[...X.querySelectorAll?.('input[name$="[_destroy]"]')??[]][0];if(Z){if(Z.value="1",typeof Z.dispatchEvent==="function")Z.dispatchEvent(new Event("input",{bubbles:!0}));X.hidden=!0}else X.parentNode?.removeChild?.(X);this.#N()}syncNestedJson(X){let Z=X?.target;if(!Z||!this.#$(Z))return;this.#N()}#N(){if(typeof this.element?.querySelectorAll!=="function")return;let X=this.#X();for(let Z of[...this.element.querySelectorAll("[data-reactive-nested-json]")].filter(X))this.#s(Z.getAttribute("data-reactive-nested-json"))}#s(X){let Z=this.#X(),$=[...this.element.querySelectorAll(`[data-reactive-nested-list="${X}"]`)].find(Z);if(!$)return;let Q=this.#NX($);if(!Q)return;let j=[];for(let G of[...$.querySelectorAll?.("[data-reactive-nested-row]")??[]]){if(!Z(G)||G.hidden)continue;j.push(this.#n(G))}let z=JSON.stringify(j);if(Q.value===z)return;if(Q.value=z,typeof Q.dispatchEvent==="function")Q.dispatchEvent(new Event("input",{bubbles:!0}))}#NX(X){let Z=X.getAttribute?.("data-reactive-nested-json-field");if(!Z)return null;let $=this.#X();return[...this.element.querySelectorAll(Z)].find($)??null}#n(X){let Z={};for(let $ of[...X.querySelectorAll?.("input, select, textarea")??[]]){let Q=this.#a($.getAttribute?.("name"));if(Q===null||Q==="_destroy")continue;Z[Q]=this.#S($)}return Z}#a(X){if(!X)return null;let Z=X.match(/\[([^\][]+)\]$/);return Z?Z[1]:X}#S(X){if(X.type==="checkbox")return X.checked?X.value||"on":"";return X.value??""}#CX(X){let Z=this.element.getAttribute(X);if(!Z)return[];try{let $=JSON.parse(Z);return Array.isArray($)?$:[]}catch{return[]}}#DX(){let X=this.element.getAttribute("data-reactive-compute-inputs-param");if(!X)return[];try{let Z=JSON.parse(X);if(Array.isArray(Z))return Z.map(($)=>[$,"number"]);if(Z&&typeof Z==="object")return Object.entries(Z);return[]}catch{return[]}}#RX(X,Z,$){let Q=X?.target;if(!Q?.name||typeof Q.closest!=="function")return null;let j=this.#FX(Q.name,$);if(!Z.includes(j))return null;return this.#$(Q)?j:null}#FX(X,Z){if(!Z)return X;let $=`${Z}[`;return X.startsWith($)&&X.endsWith("]")?X.slice($.length,-1):X}#r(X,Z){let $=String(Z);for(let Q of this.#IX(X)){if(Q.textContent===$)continue;Q.textContent=$}}#IX(X){let Z=this.element.querySelectorAll(`[data-reactive-text="${X}"]`);return Array.from(Z).filter(($)=>this.#$($))}#t(X,Z){let $=this.#TX();for(let[Q,j]of Object.entries($)){let z=Q in X?X[Q]:Z(Q)?.value;if(z===void 0||z===null)continue;let G=String(z);for(let q of Array.isArray(j)?j:[j]){if(!oX(q))continue;for(let Y of document.querySelectorAll(q)){if(Y.textContent===G)continue;Y.textContent=G}}}}#TX(){let X=this.element.getAttribute("data-reactive-compute-mirror-param");if(!X)return{};try{let Z=JSON.parse(X);return Z&&typeof Z==="object"&&!Array.isArray(Z)?Z:{}}catch{return{}}}#e(X,Z,$,Q,j,z,G){if(this.#D("reactive:before-dispatch",{action:Z,params:this.#HX($),element:this.element},{cancelable:!0}).defaultPrevented)return;let Y=Number(Q)||0;if(Y>0)return this.#kX(X,Y,Z,$,z,G);let K=Number(j)||0;if(K>0)return this.#bX(X,K,Z,$,z,G);return this.#C(Z,$,z,X,G)}#C(X,Z,$,Q,j){let z=this.#JZ($,Q),G=this.#LZ(X,Q,j),q=this.#XX()?this.#EX($,Q):null;return this.queue=(this.queue??Promise.resolve()).then(()=>this.#mX(X,Z,z,G,q)),this.queue}#EX(X,Z){if(!X?.hide)return null;let $=this.#VX(X,Z);if(!$.length)return null;return()=>{let Q=$.filter((j)=>j.isConnected&&!j.hidden);if(!Q.length)return;console.warn("[phlex-reactive] optimistic: { hide: true } was undone by the reply's re-render — "+"the element is visible again. For an instant delete, return reply.remove so the server removes it; otherwise the hide only flashes.",Q)}}#kX(X,Z,$,Q,j,z){this.#b(X);let G=()=>{this.#b(X),this.#C($,Q,j,X,z)},q=setTimeout(G,Z);X?.addEventListener?.("blur",G,{once:!0}),this.#M.set(X,{timer:q,flush:G})}#b(X){let Z=this.#M.get(X);if(!Z)return;clearTimeout(Z.timer),X?.removeEventListener?.("blur",Z.flush),this.#M.delete(X)}#SX(){for(let X of[...this.#M.keys()])this.#b(X)}#bX(X,Z,$,Q,j,z){let G=this.#H.get(X)??new Map;if(G.has($))return;let q=setTimeout(()=>{if(G.delete($),G.size===0)this.#H.delete(X)},Z);return G.set($,q),this.#H.set(X,G),this.#C($,Q,j,X,z)}#wX(){for(let X of this.#H.values())for(let Z of X.values())clearTimeout(Z);this.#H.clear()}#D(X,Z,{cancelable:$=!1}={}){let Q=new CustomEvent(X,{bubbles:!0,composed:!0,cancelable:$,detail:Z});return(this.element.isConnected?this.element:document).dispatchEvent(Q),Q}#z(X,Z,$,Q){let j=()=>{if(!this.element.isConnected){console.warn("[phlex-reactive] retry() ignored — the reactive root left the DOM");return}return this.#C(X,Z)};this.#D("reactive:error",{action:X,params:$,...Q,retry:j})}#G(X){if(this.element?.isConnected===!1)return;this.element?.setAttribute?.("data-reactive-error",X)}#hX(){this.element?.removeAttribute?.("data-reactive-error")}#yX(){let X=document.querySelector("[data-reactive-error-flash]");if(!X?.content)return;let Z=X.getAttribute("data-reactive-error-flash")||"flash",$=document.getElementById(Z);if(!$)return;$.appendChild(X.content.cloneNode(!0))}#vX(){if(typeof sessionStorage>"u")return Promise.resolve();let X=Number(sessionStorage.getItem(m));if(!Number.isFinite(X)||X<=0)return Promise.resolve();if(!P)P=!0,console.warn(`[phlex-reactive] latency simulator ACTIVE — every action is delayed by ${X}ms. Call PhlexReactive.disableLatencySim() (or clear sessionStorage) to turn it off.`);return new Promise((Z)=>setTimeout(Z,X))}#XX(){return this.element?.getAttribute?.("data-reactive-debug")==="true"}#ZX(){return typeof performance<"u"&&typeof performance.now==="function"?performance.now():Date.now()}#pX(X){if(!X)return[];let Z=[],$=/<turbo-stream\b([^>]*)>/g,Q;while((Q=$.exec(X))!==null){let j=Q[1],z=j.match(/\baction="([^"]*)"/)?.[1]??"?",G=j.match(/\btarget="([^"]*)"/)?.[1];Z.push(G?`${z} → #${G}`:z)}return Z}#fX(X){let{action:Z,status:$,ms:Q}=X,z=`reactive ${this.element?.id?`#${this.element.id} `:""}${Z} → ${$??"—"} (${Math.round(Q)}ms)`;if(console.groupCollapsed(z),console.log(`params: [${X.paramNames.join(", ")}] + collected: [${X.fieldNames.join(", ")}]`),console.log(`encoding: ${X.encoding}`),X.streams.length)console.log(`streams: ${X.streams.join(" ")}`);console.log(`token: ${X.tokenRefreshed?"refreshed ✓":"unchanged"}`),console.groupEnd()}async#mX(X,Z,$,Q,j){let{fields:z,files:G}=this.#jX(),q=this.#HX(Z),Y={...z,...q},K=this.#q,W=G.length>0,B=W?this.#HZ(K,X,Y,G):JSON.stringify({token:K,act:X,params:Y}),J=this.#XX()?{action:X,paramNames:Object.keys(q),fieldNames:Object.keys(z),encoding:W?"multipart":"json",status:null,streams:[],tokenRefreshed:!1,started:this.#ZX()}:null;await this.#vX();try{if(navigator.onLine===!1){this.#Y($),this.#G("offline"),this.#z(X,Z,Y,{kind:"offline"});return}let L;try{let V={Accept:"text/vnd.turbo-stream.html","X-CSRF-Token":this.#PZ()};if(!W)V["Content-Type"]="application/json";let O=this.#NZ();if(O)V["X-Pgbus-Connection"]=O;L=await fetch(this.#OZ(),{method:"POST",headers:V,body:B,credentials:"same-origin",signal:AbortSignal.timeout(this.#xZ())})}catch(V){if(console.error("[phlex-reactive] action error",V),this.#Y($),V?.name==="TimeoutError"||V?.name==="AbortError"){this.#G("timeout"),this.#z(X,Z,Y,{kind:"timeout"});return}this.#yX(),this.#G("network"),this.#z(X,Z,Y,{kind:"network"});return}if(J)J.status=L.status;if(L.redirected){console.error("[phlex-reactive] action was redirected (auth/CSRF?) — no update applied"),this.#Y($),this.#G("redirected"),this.#z(X,Z,Y,{kind:"redirected",status:L.status});return}if(!L.ok){let V=await L.text();if(console.error(`[phlex-reactive] action failed: HTTP ${L.status}`,V),this.#Y($),(L.headers.get("Content-Type")||"").includes("turbo-stream")){let O=this.#QX(V);if(this.#q=O??this.#q,J)this.#$X(J,V,O);window.Turbo.renderStreamMessage(V)}this.#G("http"),this.#z(X,Z,Y,{kind:"http",status:L.status,body:V});return}let H=L.headers.get("Content-Type")||"";if(!H.includes("turbo-stream")){console.error(`[phlex-reactive] expected a turbo-stream, got "${H}" — no update applied`),this.#Y($),this.#G("content-type"),this.#z(X,Z,Y,{kind:"content-type",status:L.status});return}let U=await L.text(),M=this.#QX(U);if(this.#q=M??this.#q,J)this.#$X(J,U,M);if(window.Turbo.renderStreamMessage(U),j)queueMicrotask(j);this.#hX(),this.#D("reactive:applied",{action:X,params:Y,html:U})}catch(L){console.error("[phlex-reactive] action error",L),this.#Y($),this.#D("reactive:error",{action:X,params:Y,kind:"apply"})}finally{if(Q?.(),J)this.#fX({...J,ms:this.#ZX()-J.started})}}#$X(X,Z,$){X.streams=this.#pX(Z),X.tokenRefreshed=$!=null}get#q(){return this.#m??this.tokenValue}set#q(X){this.#m=X}#QX(X){let Z=this.element.id;if(!Z)return X.match(/data-reactive-token-value="([^"]+)"/)?.[1];let{token:$,self:Q}=this.#uX(Z),j=X.match($);if(j)return j[1];let z=X.match(Q);if(z)return z[1].match(/data-reactive-token-value="([^"]+)"/)?.[1];return}#uX(X){let Z=this.#u;if(Z&&Z.id===X)return Z;let $=gX(X);return this.#u={id:X,token:new RegExp(`<turbo-stream\\b[^>]*\\baction="reactive:token"[^>]*\\btarget="${$}"[^>]*\\bdata-reactive-token-value="([^"]+)"`),self:new RegExp(`<turbo-stream\\b[^>]*\\baction="(?:replace|update)"[^>]*\\btarget="${$}"[^>]*>([\\s\\S]*?)</turbo-stream>`)}}#$(X){return X.closest('[data-controller~="reactive"]')===this.element}#X(){if(this.element.querySelectorAll('[data-controller~="reactive"]').length===0)return()=>!0;return(Z)=>this.#$(Z)}#w(X,Z){if(X)return X;if(!Z)return null;let $=Z;if(typeof Z==="string")try{$=JSON.parse(Z)}catch{return console.warn(`[phlex-reactive] malformed conditional confirm payload ${JSON.stringify(Z)} — skipped`),null}if(!$||typeof $!=="object")return null;let{fields:Q}=this.#jX(),j=(G)=>Q[G],z;if(typeof $.predicate==="string"){let G=LX($.predicate);if(!G)return console.warn(`[phlex-reactive] confirm predicate "${$.predicate}" is not registered — proceeding without a dialog (register it with setConfirmPredicate)`),null;z=!!G(Q)}else z=v($.groups?.any,j)===!0;return z?$.message:null}#jX(){let X={},Z=[],$=this.#X();return this.element.querySelectorAll("input[name], select[name], textarea[name]").forEach((Q)=>{if(!$(Q))return;if(Q.type==="file")for(let j of Q.files??[])Z.push({name:Q.name,file:j,multiple:Q.multiple});else if(Q.type==="checkbox")X[Q.name]=Q.checked;else if(Q.type==="radio"){if(Q.checked)X[Q.name]=Q.value}else X[Q.name]=Q.value}),this.element.querySelectorAll("[name]:is(lexxy-editor, trix-editor, [contenteditable=''], [contenteditable=true], [contenteditable=plaintext-only])").forEach((Q)=>{if(!$(Q))return;let j=Q.getAttribute("name");if(!j)return;let z=X[j];if(z==null||z==="")X[j]=Q.value??Q.textContent??Q.innerHTML??""}),{fields:X,files:Z}}#h(){if(typeof this.element?.querySelectorAll!=="function")return;let X=0;if(this.element.querySelectorAll("input[name], select[name], textarea[name]").forEach((Z)=>{if(!this.#$(Z))return;if(Z.type==="file")return;if(this.#gX(Z))Z.setAttribute("data-reactive-dirty","true"),X++;else Z.removeAttribute("data-reactive-dirty")}),X>0)this.element.setAttribute("data-reactive-dirty",String(X));else this.element.removeAttribute("data-reactive-dirty")}#gX(X){if(X.type==="checkbox"||X.type==="radio")return X.checked!==X.defaultChecked;if(X.tag==="select"||X.options)return Array.from(X.options??[]).some((Z)=>Z.selected!==Z.defaultSelected);return X.value!==X.defaultValue}#zX(){let X=this.element.getAttribute?.("data-reactive-dirty"),Z=Number(X);return Number.isFinite(Z)&&Z>0?Z:0}#cX(){if(typeof window>"u"||typeof window.addEventListener!=="function")return;this.#J=(X)=>{if(this.#zX()===0)return;return X.preventDefault(),X.returnValue="You have unsaved changes.",X.returnValue},this.#L=(X)=>{if(this.#zX()===0)return;if(!(typeof window.confirm==="function"?window.confirm("You have unsaved changes. Leave anyway?"):!0))X.preventDefault?.()},window.addEventListener("beforeunload",this.#J),window.addEventListener("turbo:before-visit",this.#L)}#dX(){if(this.#W)this.element.removeEventListener?.("turbo:morph-element",this.#W),this.#W=void 0;if(typeof window<"u"&&typeof window.removeEventListener==="function"){if(this.#J)window.removeEventListener("beforeunload",this.#J);if(this.#L)window.removeEventListener("turbo:before-visit",this.#L)}this.#J=void 0,this.#L=void 0}#lX(){if(this.element.getAttribute?.("data-reactive-show-targets"))return!0;let X=this.element.querySelectorAll?.(i)??[];for(let Z of X)if(this.#$(Z))return!0;return!1}#GX(){if(typeof this.element?.querySelectorAll!=="function")return;let X=this.#X(),Z=this.element.getAttribute?.("data-reactive-scope")||null,$=new Map,Q=(j)=>{if(!$.has(j))$.set(j,this.#nX(j,X,Z));return $.get(j)};for(let j of this.element.querySelectorAll(i)){if(!X(j))continue;let z=j.getAttribute("data-reactive-show");if(z!==null){let K=rX(nX(z),Q);if(K!==null)this.#qX(j,K,X,Z);continue}let G=j.getAttribute("data-reactive-show-field");if(!G)continue;let q=Q(G);if(q===null)continue;let Y=sX(j,q);if(Y===null)continue;this.#qX(j,Y,X,Z)}this.#iX(Q)}#qX(X,Z,$,Q){if(X.hidden=!Z,X.getAttribute("data-reactive-show-disable")!=="true")return;if(typeof X.querySelectorAll!=="function")return;for(let j of X.querySelectorAll("input[name], select[name], textarea[name]"))if($(j))j.disabled=!Z;if(X.name&&$(X))X.disabled=!Z}#iX(X){let Z=this.#sX();for(let[$,Q]of Object.entries(Z)){if($.startsWith("#")){this.#oX($,Q,X);continue}if(!Q||typeof Q!=="object"||Array.isArray(Q))continue;let j=X($);if(j===null)continue;let z=()=>j;for(let[G,q]of Object.entries(Q)){if(!o(G))continue;let Y;if(Array.isArray(q)){if(q.length===0){console.warn(`[phlex-reactive] malformed reactive_show_targets group for ${G} — skipped`);continue}Y=q.every((K)=>u(K,z))}else{let K=KX(q,j);if(K===null){console.warn(`[phlex-reactive] malformed reactive_show_targets predicate for ${G} — skipped`);continue}Y=K}for(let K of document.querySelectorAll(G))K.hidden=!Y}}}#oX(X,Z,$){if(!o(X))return;let Q=Z&&typeof Z==="object"&&!Array.isArray(Z)?Z.any:null,j=aX(Q);if(j===null){console.warn(`[phlex-reactive] malformed reactive_show_targets conditions for ${X} — skipped`);return}if(j.every((G)=>$(G)===null))return;let z=v(Q,$);if(z===null)return;for(let G of document.querySelectorAll(X))G.hidden=!z}#sX(){let X=this.element.getAttribute?.("data-reactive-show-targets");if(!X)return{};try{let Z=JSON.parse(X);if(Z&&typeof Z==="object"&&!Array.isArray(Z))return Z}catch{}return console.warn("[phlex-reactive] malformed data-reactive-show-targets — ignored. "+"Did two reactive_show_targets calls collide on one root? Declare every field in ONE call: reactive_show_targets(mode: { ... }, kind: { ... })"),{}}#nX(X,Z,$){let Q=$&&!X.includes("[")?`${$}[${X}]`:X,j=!1,z=null;for(let G of this.element.querySelectorAll(`[name="${Q}"]`)){if(!Z(G))continue;if(G.type==="checkbox")return G.checked?"true":"false";if(G.type==="radio"){if(G.checked)return G.value??"";j=!0;continue}z??=G}if(z)return z.value??"";return j?"":null}#aX(){if(!this.#Z)return;this.element.removeEventListener?.("input",this.#Z),this.element.removeEventListener?.("change",this.#Z),this.element.removeEventListener?.("turbo:morph-element",this.#Z),this.#Z=void 0}#R(){return!!(this.element.getAttribute?.("data-reactive-filter-input")&&this.element.getAttribute?.("data-reactive-filter-option"))}#rX(){return this.element.getAttribute?.("data-reactive-compute-seed")==="true"}#tX(X){let Z=this.element.getAttribute("data-reactive-filter-input");return!!Z&&typeof X.target?.matches==="function"&&X.target.matches(Z)}#A(){if(typeof this.element?.querySelectorAll!=="function")return;let X=this.element.getAttribute("data-reactive-filter-input"),Z=this.element.getAttribute("data-reactive-filter-option");if(!X||!Z)return;let $=this.#X(),Q=[...this.element.querySelectorAll(X)].find($);if(!Q)return;let j=(Q.value??"").trim().toLowerCase(),z=0;for(let Y of this.element.querySelectorAll(Z)){if(!$(Y))continue;let K=(Y.getAttribute("data-reactive-filter-text")??Y.textContent??"").toLowerCase(),W=Y.hasAttribute?.("data-reactive-tags-selected")||j!==""&&!K.includes(j);if(Y.hidden=W,W)Y.removeAttribute("data-reactive-highlighted");else z++}let G=this.element.getAttribute("data-reactive-filter-group");if(G)for(let Y of this.element.querySelectorAll(G)){if(!$(Y))continue;let K=[...Y.querySelectorAll(Z)].filter($);if(K.length===0)continue;Y.hidden=K.every((W)=>W.hidden)}let q=this.element.getAttribute("data-reactive-filter-empty");if(q){for(let Y of this.element.querySelectorAll(q))if($(Y))Y.hidden=z>0}}#eX(){if(!this.#Q)return;this.element.removeEventListener?.("input",this.#Q),this.element.removeEventListener?.("turbo:morph-element",this.#Q),this.#Q=void 0}#F(){return!!this.element.getAttribute?.("data-reactive-tags-field")}#XZ(){if(typeof this.element?.querySelector!=="function")return!1;return!!this.element.querySelector("[data-reactive-nested-json]")}#y(){if(typeof this.element?.querySelectorAll!=="function")return null;let X=this.element.getAttribute("data-reactive-tags-field");if(!X)return null;let Z=this.#X();return[...this.element.querySelectorAll(X)].find(Z)??null}#v(X){let Z=new Set,$=[];for(let Q of String(X.value??"").split(",")){let j=Q.trim();if(j===""||Z.has(j.toLowerCase()))continue;Z.add(j.toLowerCase()),$.push(j)}return $}#YX(X){let Z=this.#y();if(!Z)return!1;let $=this.#v(Z),Q=new Set($.map((z)=>z.toLowerCase())),j=!1;for(let z of X){let G=String(z??"").trim();if(G===""||Q.has(G.toLowerCase()))continue;Q.add(G.toLowerCase()),$.push(G),j=!0}if(j)this.#KX(Z,$);return j}#KX(X,Z){if(X.value=Z.join(","),typeof X.dispatchEvent==="function")X.dispatchEvent(new Event("input",{bubbles:!0}));this.#p()}#ZZ(){if(typeof this.element?.querySelectorAll!=="function")return null;let X=this.element.getAttribute("data-reactive-filter-input");if(!X)return null;let Z=this.#X();return[...this.element.querySelectorAll(X)].find(Z)??null}#p(){let X=this.#y();if(!X)return;let Z=this.#v(X),$=this.#X();this.#$Z(Z,$),this.#QZ(Z,$)}#$Z(X,Z){let $=[...this.element.querySelectorAll("[data-reactive-tags-list]")].find(Z);if(!$)return;let j=[...this.element.querySelectorAll("[data-reactive-tags-template]")].find(Z)?.content?.firstElementChild;if(!j){if(!this.#c)console.warn("[phlex-reactive] reactive_tags: no chip <template data-reactive-tags-template> found in this root — "+"chips will not render (the hidden field still updates). Add a template with a [data-reactive-tag-text] node and a reactive_tags_remove button."),this.#c=!0;return}while($.firstChild)$.removeChild($.firstChild);for(let z of X){let G=j.cloneNode(!0);G.setAttribute?.("data-reactive-tag",z);let q=G.matches?.("[data-reactive-tag-text]")?G:(G.querySelectorAll?.("[data-reactive-tag-text]")??[])[0];if(q)q.textContent=z;let Y=[...G.querySelectorAll?.('[data-action*="reactive#tagsRemove"]')??[]];if(G.matches?.('[data-action*="reactive#tagsRemove"]'))Y.push(G);for(let K of Y)K.setAttribute?.("data-reactive-tag-param",z);$.appendChild(G)}}#QZ(X,Z){let $=new Set(X.map((Q)=>Q.toLowerCase()));for(let Q of this.element.querySelectorAll("[role=option]")){if(!Z(Q))continue;let j=Q.getAttribute?.("data-reactive-tag-param");if(!j)continue;if($.has(j.toLowerCase()))Q.setAttribute("data-reactive-tags-selected","true"),Q.hidden=!0,Q.removeAttribute?.("data-reactive-highlighted");else if(Q.hasAttribute?.("data-reactive-tags-selected")){if(Q.removeAttribute("data-reactive-tags-selected"),!this.#R())Q.hidden=!1}}if(this.#R())this.#A()}#jZ(){return this.#k=Math.max(this.#k+1,Date.now()),this.#k}#zZ(X,Z){let $=[X,...X.querySelectorAll?.("*")??[]];for(let Q of $)for(let j of["name","id","for"]){let z=Q.getAttribute?.(j);if(z&&z.includes("NEW_ROW"))Q.setAttribute?.(j,z.replaceAll("NEW_ROW",String(Z)))}}#GZ(X){if(this.#d)return;console.warn(`[phlex-reactive] nested rows: no owned [data-reactive-nested-list="${X}"] container + <template data-reactive-nested-template="${X}"> pair found in this root — the add `+"trigger did nothing. Render both inside the same reactive root (reactive_nested_list / reactive_nested_template)."),this.#d=!0}#qZ(){if(!this.#V)return;this.element.removeEventListener?.("turbo:morph-element",this.#V),this.#V=void 0}#YZ(){if(this.#j)this.element.removeEventListener?.("input",this.#j),this.element.removeEventListener?.("change",this.#j),this.#j=void 0;if(this.#B)this.element.removeEventListener?.("turbo:morph-element",this.#B),this.#B=void 0}#KZ(){if(!this.#_)return;this.element.removeEventListener?.("turbo:morph-element",this.#_),this.#_=void 0}#HZ(X,Z,$,Q){let j=new FormData;j.append("token",X),j.append("act",Z);for(let[G,q]of Object.entries($))this.#f(j,`params[${G}]`,q);let z=this.#UZ(Q);for(let{name:G,file:q,multiple:Y}of Q){let W=Y||z.has(G)?`params[${G}][]`:`params[${G}]`;j.append(W,q,q.name)}return j}#f(X,Z,$){if($==null)X.append(Z,"");else if(Array.isArray($))$.forEach((Q,j)=>this.#f(X,`${Z}[${j}]`,Q));else if(typeof $==="object")for(let[Q,j]of Object.entries($))this.#f(X,`${Z}[${Q}]`,j);else X.append(Z,String($))}#UZ(X){let Z=new Map;for(let{name:$}of X)Z.set($,(Z.get($)??0)+1);return new Set([...Z].filter(([,$])=>$>1).map(([$])=>$))}#HX(X){if(!X)return{};try{return typeof X==="string"?JSON.parse(X):X}catch{return{}}}#UX(X){return HX(X)}#WX(X){UX(X,(Z)=>this.#JX(Z))}#JX(X){let Z=X.to;if(Z==="@root")return[this.element];if(typeof Z!=="string"||Z==="")return[];if(X.global)return[...document.querySelectorAll(Z)];return[...this.element.querySelectorAll(Z)].filter(($)=>this.#$($))}#WZ(X,Z){if(X?.checked!=="keep")return!1;let $=Z?.type;return $==="checkbox"||$==="radio"}#JZ(X,Z){if(!X)return null;let $=this.#LX(X,Z,!0);return $.length?$:null}#Y(X){if(!X)return;if(!this.element.isConnected)return;for(let Z of X)Z()}#LZ(X,Z,$){this.#BZ(X,Z);let Q=$?this.#LX($,Z,!1):[];QX();let j=!1;return()=>{if(j)return;j=!0,this.#_Z(X,Z),jX();for(let z of Q)z()}}#LX(X,Z,$){let Q=[];for(let j of this.#VX(X,Z)){if(X.add_class){let z=X.add_class.filter((G)=>!j.classList.contains(G));if(j.classList.add(...z),z.length)Q.push(()=>j.classList.remove(...z))}if(X.remove_class){let z=X.remove_class.filter((G)=>j.classList.contains(G));if(j.classList.remove(...z),z.length)Q.push(()=>j.classList.add(...z))}if(X.toggle_class)X.toggle_class.forEach((z)=>j.classList.toggle(z)),Q.push(()=>X.toggle_class.forEach((z)=>j.classList.toggle(z)));if(X.hide)j.hidden=!0,Q.push(()=>j.hidden=!1);if(X.show)j.hidden=!1,Q.push(()=>j.hidden=!0)}if(Z&&(X.disable||X.text!=null))Q.push(this.#VZ(X,Z));if($&&X.checked==="keep"&&Z&&"checked"in Z){let j=Z.checked;Q.push(()=>Z.checked=!j)}return Q}#VX(X,Z){if(X.to==null)return Z?[Z]:[];return this.#JX({to:X.to})}#VZ(X,Z){let $=this.#O.get(Z);if($)$.count++;else this.#O.set(Z,{count:1,disabled:Z.disabled,html:Z.innerHTML,hadText:X.text!=null,swappedTo:X.text});if(X.disable)Z.disabled=!0;if(X.text!=null)Z.innerHTML=X.text;return()=>this.#AZ(Z,X)}#BZ(X,Z){if(this.#K(Z,X,1),this.#K(this.element,X,1),this.#U.set(X,(this.#U.get(X)??0)+1),this.#T++===0)this.element.setAttribute("aria-busy","true");for(let $ of this.#BX(X))this.#K($,X,1)}#_Z(X,Z){this.#K(Z,X,-1),this.#K(this.element,X,-1);let $=(this.#U.get(X)??1)-1;if($<=0)this.#U.delete(X);else this.#U.set(X,$);if(--this.#T<=0)this.#T=0,this.element.removeAttribute("aria-busy");for(let Q of this.#BX(X))this.#K(Q,X,-1)}#K(X,Z,$){if(!X||typeof X.getAttribute!=="function")return;let Q=this.#E.get(X)??new Map,j=(Q.get(Z)??0)+$;if(j<=0)Q.delete(Z);else Q.set(Z,j);if(Q.size===0){this.#E.delete(X),X.removeAttribute("data-reactive-busy");return}this.#E.set(X,Q),X.setAttribute("data-reactive-busy",[...Q.keys()].join(" "))}#BX(X){return[...this.element.querySelectorAll?.("[data-reactive-busy-on]")??[]].filter(($)=>$.getAttribute("data-reactive-busy-on")===X&&this.#$($))}#AZ(X,Z){let $=this.#O.get(X);if(!$)return;if(--$.count>0)return;if(this.#O.delete(X),!X.isConnected)return;if(Z.disable)X.disabled=$.disabled;if($.hadText&&X.innerHTML===$.swappedTo)X.innerHTML=$.html}#MZ(X){if(!X||typeof X!=="object")return null;let{class:Z,...$}=X;return Z==null?X:{...$,add_class:Z}}#OZ(){return this.#_X??=document.querySelector('meta[name="phlex-reactive-action-path"]')?.content||"/reactive/actions"}#xZ(){if(this.#I!=null)return this.#I;let X=document.querySelector('meta[name="phlex-reactive-timeout"]')?.content,Z=Number(X);return this.#I=Number.isFinite(Z)&&Z>0?Z:30000}#PZ(){return document.querySelector('meta[name="csrf-token"]')?.content??""}#NZ(){return document.querySelector("pgbus-stream-source[connection-id]")?.getAttribute("connection-id")||document.querySelector('meta[name="pgbus-connection-id"]')?.content||null}}export{GZ as resetReactiveDefers,JZ as resetReactiveActivity,VX as registerReactiveVisit,BX as registerReactiveToken,pX as registerReactiveOffline,_X as registerReactiveJs,IX as registerReactiveEffects,DX as registerReactiveDismiss,AX as registerReactiveDefer,d as registerReactiveActions,WZ as reactiveActivityCount,qZ as pendingDeferVia,jX as exitReactiveActivity,gX as escapeRegExp,QX as enterReactiveActivity,fX as enableLatencySim,mX as disableLatencySim,BZ as default,cX as checkReactiveRegistration,LZ as __resetReactiveRegistrationForTest,HZ as __resetReactiveOfflineForTest,UZ as __resetReactiveLatencyForTest,KZ as __resetReactiveEffectsForTest,YZ as __resetReactiveDismissForTest,VZ as __markReactiveConnectedForTest,m as LATENCY_KEY,$X as ACTIVE_ATTR};
|
|
1
|
+
import{Controller as WX}from"@hotwired/stimulus";import{confirmResolver as I}from"phlex/reactive/confirm";import{computeReducer as JX}from"phlex/reactive/compute";import{confirmPredicate as LX}from"phlex/reactive/confirm_predicate";function VX(){let X=window.Turbo?.StreamActions;if(!X||X["reactive:visit"])return;X["reactive:visit"]=function(){let Z=this.getAttribute("data-url");if(Z)window.Turbo.visit(Z,{action:"advance"})}}function BX(){let X=window.Turbo?.StreamActions;if(!X||X["reactive:token"])return;X["reactive:token"]=function(){let Z=this.getAttribute("data-reactive-token-value"),$=this.getAttribute("target");if(!Z||!$)return;let Q=document.getElementById($);if(Q)Q.setAttribute("data-reactive-token-value",Z)}}function _X(){let X=window.Turbo?.StreamActions;if(!X||X["reactive:js"])return;X["reactive:js"]=function(){let Z=UX(this.getAttribute("data-reactive-ops"));if(!Z.length)return;let $=this.getAttribute("target"),Q=$?document.getElementById($):null;if($&&!Q)return;N(Z,(j)=>qZ(j,Q))}}var _=new Map;function n(X,Z){let $=!_.has(X);if(_.set(X,Z),$)jX()}function R(X){if(_.delete(X))zX()}function UZ(){_.clear(),w=!1}function WZ(X){return _.get(X)?.via}var w=!1;function MX(){let X=window.Turbo?.StreamActions;if(!X||X["reactive:defer"])return;if(X["reactive:defer"]=function(){let Z=this.getAttribute("target");if(!Z)return;if(this.getAttribute("data-reactive-defer-via")==="stream"){xX(Z,this);return}let $=this.getAttribute("data-reactive-defer-token");if(!$)return;F(Z,$)},!w&&typeof document<"u"&&document.addEventListener)w=!0,document.addEventListener("turbo:before-stream-render",AX)}function AX(X){let $=X.target?.getAttribute?.("target");if(!$)return;let Q=$.startsWith("reactive-defer-src-")?$.slice(19):$;if(_.get(Q)?.via==="stream")R(Q)}function F(X,Z){let $=document.getElementById(X);if(!$){console.warn(`[phlex-reactive] reactive:defer target #${X} is not on the page — skipped`);return}r(X),t($);let Q={via:"fetch",abort:new AbortController,timedOut:!1};n(X,Q),PX(X,Q,Z)}function xX(X,Z){let $=document.getElementById(X);if(!$){console.warn(`[phlex-reactive] reactive:defer target #${X} is not on the page — skipped`);return}let Q=Z.getAttribute("data-reactive-defer-src");if(!Q)return;if(!globalThis.customElements?.get?.("pgbus-stream-source")){let z=Z.getAttribute("data-reactive-defer-token");if(z){F(X,z);return}console.error("[phlex-reactive] reactive:defer via=stream but <pgbus-stream-source> is not registered "+"and no fallback token was provided — is the pgbus client loaded on this page?");return}r(X),t($);let j=document.createElement("pgbus-stream-source");j.id=a(X),j.setAttribute("src",Q),j.setAttribute("since-id",Z.getAttribute("data-reactive-defer-since-id")??"0"),j.setAttribute("hidden",""),document.body.appendChild(j),n(X,{via:"stream"})}function a(X){return`reactive-defer-src-${X}`}async function PX(X,Z,$){let Q=setTimeout(()=>{Z.timedOut=!0,Z.abort.abort()},DX()),j;try{j=await fetch(NX(),{method:"POST",headers:{Accept:"text/vnd.turbo-stream.html","Content-Type":"application/json","X-CSRF-Token":OX()},body:JSON.stringify({token:$}),credentials:"same-origin",signal:Z.abort.signal})}catch(q){if(clearTimeout(Q),_.get(X)!==Z)return;console.error("[phlex-reactive] deferred render failed",q),T(X,$);return}if(_.get(X)!==Z){clearTimeout(Q);return}if(j.status===204){clearTimeout(Q),c(X);return}if(!j.ok){clearTimeout(Q),console.error(`[phlex-reactive] deferred render failed: HTTP ${j.status}`),T(X,$,j.status);return}let z;try{z=await j.text()}catch(q){if(clearTimeout(Q),_.get(X)!==Z)return;console.error("[phlex-reactive] deferred render failed reading the body",q),T(X,$);return}if(clearTimeout(Q),_.get(X)!==Z)return;c(X),window.Turbo.renderStreamMessage(z)}function r(X){let Z=_.get(X);if(!Z)return;if(R(X),Z.via==="fetch")Z.abort.abort();else document.getElementById(a(X))?.remove?.()}function t(X){X.setAttribute("data-reactive-defer-pending","true"),X.setAttribute("aria-busy","true")}function e(X){X.removeAttribute("data-reactive-defer-pending"),X.removeAttribute("aria-busy")}function c(X){R(X);let Z=document.getElementById(X);if(!Z)return;e(Z),Z.removeAttribute("data-reactive-error")}function T(X,Z,$){R(X);let Q=document.getElementById(X);if(!Q)return;e(Q),Q.setAttribute("data-reactive-error","defer");let j=()=>{let z=document.getElementById(X);if(!z){console.warn("[phlex-reactive] defer retry() ignored — the target left the DOM");return}z.removeAttribute("data-reactive-error"),F(X,Z)};Q.dispatchEvent(new CustomEvent("reactive:error",{bubbles:!0,composed:!0,detail:{kind:"defer",target:X,status:$,retry:j}}))}function NX(){return document.querySelector('meta[name="phlex-reactive-defer-path"]')?.content||"/reactive/defer"}function OX(){return document.querySelector('meta[name="csrf-token"]')?.content??""}function DX(){let X=document.querySelector('meta[name="phlex-reactive-timeout"]')?.content,Z=Number(X);return Number.isFinite(Z)&&Z>0?Z:30000}var h=!1;function RX(){if(h)return;if(typeof document>"u"||!document.addEventListener)return;h=!0,document.addEventListener("turbo:before-stream-render",FX)}function FX(X){let Z=X.detail,$=Z?.render;if(typeof $!=="function"||$.__reactiveDismissWrapped){if(typeof requestAnimationFrame==="function")requestAnimationFrame(E);else setTimeout(E,0);return}let Q=async(j)=>{await $(j),E()};Q.__reactiveDismissWrapped=!0,Z.render=Q}function E(){let X=document.querySelectorAll("[data-reactive-dismiss-after]");for(let Z of X){if(Z.hasAttribute("data-reactive-dismiss-scheduled"))continue;let $=Number(Z.getAttribute("data-reactive-dismiss-after"));if(!Number.isFinite($)||$<=0)continue;Z.setAttribute("data-reactive-dismiss-scheduled",""),setTimeout(()=>Z.remove(),$)}}function JZ(){h=!1}var CX=Object.freeze({append:"enter",prepend:"enter",replace:"update",update:"update",remove:"exit"}),k=Object.freeze(["fade","slide","scale","highlight","shake"]),y="data-reactive-fx-pending",XX=1000,v=!1;function IX(){if(v)return;if(typeof document>"u"||typeof document.addEventListener!=="function")return;v=!0,document.addEventListener("turbo:before-stream-render",TX)}function LZ(){v=!1}function TX(X){let Z=X.detail,$=Z?.render;if(typeof $!=="function"||$.__reactiveEffectsWrapped)return;let Q=Z?.newStream??X.target,j=CX[Q?.getAttribute?.("action")];if(!j||SX())return;let z=EX(Q,j);if(!z)return;let q=j==="exit"?async(G)=>{await hX(O(Q),z),await $(G)}:async(G)=>{let Y=j==="enter"?bX(Q):null;if(await $(G),j==="enter")wX(Y,z);else ZX(O(Q),z)};q.__reactiveEffectsWrapped=!0,Z.render=q}function EX(X,Z){let $=X.getAttribute?.("data-reactive-effect");if($==="off")return null;if($)return d($,Z);let j=(Z==="enter"?kX(X):O(X))?.getAttribute?.(`data-reactive-effect-${Z}`);return j?d(j,Z):null}function O(X){let Z=X.getAttribute?.("target");return Z?document.getElementById?.(Z)??null:null}function kX(X){return X.querySelector?.("template")?.content?.firstElementChild??null}function d(X,Z){if(X.startsWith("[")){let Q=null;try{let j=JSON.parse(X);if(Array.isArray(j)&&j.length===3)Q=j.map(String)}catch{}if(Q)return{legs:Q};return console.warn(`[phlex-reactive] malformed effect legs ${JSON.stringify(X)} — skipped`),null}let $=X==="random"?k[Math.floor(Math.random()*k.length)]:X;if(!k.includes($))return console.warn(`[phlex-reactive] unknown effect ${JSON.stringify(X)} — skipped`),null;return{className:`reactive-fx--${$}-${Z}`}}function SX(){try{return typeof matchMedia==="function"&&matchMedia("(prefers-reduced-motion: reduce)").matches}catch{return!1}}function bX(X){let Z=X.querySelector?.("template")?.content;if(!Z)return null;for(let $ of Array.from(Z.children??[]))$.setAttribute?.(y,"");return O(X)}function wX(X,Z){if(typeof X?.querySelectorAll!=="function")return;for(let $ of Array.from(X.querySelectorAll(`[${y}]`)))$.removeAttribute(y),ZX($,Z)}async function hX(X,Z){if(!X?.classList)return;if(Z.legs){await $X(X,Z.legs);return}X.classList.add(Z.className);let $=p(X);if($<=0){X.classList.remove(Z.className);return}await u(X,$),X.classList.remove(Z.className)}function ZX(X,Z){if(!X?.classList)return;if(Z.legs){$X(X,Z.legs);return}if(X.classList.contains(Z.className))X.classList.remove(Z.className),X.offsetWidth;X.classList.add(Z.className);let $=p(X);if($<=0){X.classList.remove(Z.className);return}let Q=X.__reactiveFxToken=(X.__reactiveFxToken??0)+1;u(X,$).then(()=>{if(X.__reactiveFxToken===Q)X.classList.remove(Z.className)})}async function $X(X,Z){let[$,Q,j]=Z.map(yX),z=X.__reactiveFxToken=(X.__reactiveFxToken??0)+1;if(X.classList.remove(...$,...Q,...j),X.classList.add(...$,...Q),await vX(),X.__reactiveFxToken!==z)return;X.classList.remove(...Q),X.classList.add(...j);let q=p(X);if(q>0)await u(X,q);if(X.__reactiveFxToken!==z)return;X.classList.remove(...$,...j)}function yX(X){return String(X??"").split(/\s+/).filter(Boolean)}function p(X){if(typeof getComputedStyle!=="function")return 0;try{let Z=getComputedStyle(X),$=(z)=>String(z??"").split(",").reduce((q,G)=>Math.max(q,parseFloat(G)||0),0),Q=$(Z.animationDuration)+$(Z.animationDelay),j=$(Z.transitionDuration)+$(Z.transitionDelay);return Math.min(Math.max(Q,j)*1000,XX)}catch{return 0}}function u(X,Z){return new Promise(($)=>{let Q=!1,j=()=>{if(Q)return;Q=!0,$()};X.addEventListener?.("animationend",j,{once:!0}),X.addEventListener?.("transitionend",j,{once:!0}),setTimeout(j,Math.min(Z+50,XX))})}function vX(){return new Promise((X)=>{if(typeof requestAnimationFrame==="function")requestAnimationFrame(()=>X());else setTimeout(X,16)})}var f=!1;function fX(){if(f)return;if(typeof window>"u"||typeof document>"u")return;if(typeof window.addEventListener!=="function")return;f=!0;let X=()=>{let Z=document.documentElement;if(typeof Z?.toggleAttribute!=="function")return;Z.toggleAttribute("data-reactive-offline",globalThis.navigator?.onLine===!1)};X(),window.addEventListener("online",X),window.addEventListener("offline",X)}function VZ(){f=!1}var m="phlex-reactive:latency",D=!1;function pX(X){if(typeof sessionStorage>"u")return;sessionStorage.setItem(m,String(X))}function uX(){if(typeof sessionStorage>"u")return;sessionStorage.removeItem(m),D=!1}function mX(){if(typeof window>"u"||typeof document>"u")return;if(document.querySelector?.('meta[name="phlex-reactive-env"]')?.content!=="development")return;window.PhlexReactive={enableLatencySim:pX,disableLatencySim:uX}}function BZ(){D=!1}var QX="data-reactive-active",M=0;function jX(){if(M++,M===1)qX("reactive:busy")}function zX(){if(M===0)return;if(M--,M===0)qX("reactive:idle")}function _Z(){return M}function MZ(){M=0,(typeof document<"u"?document.documentElement:null)?.removeAttribute?.(QX)}function qX(X){if(typeof document>"u")return;let Z=document.documentElement;if(typeof Z?.toggleAttribute==="function")Z.toggleAttribute(QX,M>0);if(typeof document.dispatchEvent==="function"&&typeof CustomEvent==="function")document.dispatchEvent(new CustomEvent(X,{detail:{count:M}}))}function l(){VX(),BX(),_X(),MX(),RX(),IX(),fX(),mX()}function gX(X){return X.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}if(typeof window<"u")if(window.Turbo)l();else document.addEventListener("turbo:load",l,{once:!0});var C=!1;function cX(){if(C)return;if(typeof document>"u")return;let X=document.querySelectorAll('[data-controller~="reactive"]');if(!X||X.length===0)return;console.warn("[phlex-reactive] found "+X.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.')}function AZ(){C=!1}function xZ(){C=!0}if(typeof window<"u"&&typeof document<"u"){let X=()=>setTimeout(cX,0);if(document.readyState==="loading")document.addEventListener("DOMContentLoaded",X,{once:!0});else X()}var dX=new Set(["href","src","srcdoc","action","formaction","xlink:href","style"]);function lX(X){let Z=String(X).toLowerCase();return Z.startsWith("on")||dX.has(Z)}function iX(X,Z,$){let[Q,j,z]=Z;X.classList.add(Q,j),$(),requestAnimationFrame(()=>{X.classList.remove(j),X.classList.add(z)});let q=!1,G=()=>{if(q)return;q=!0,X.classList.remove(Q,z)};X.addEventListener("animationend",G,{once:!0}),setTimeout(G,350)}var i=Object.freeze({show:(X,Z)=>S(X,!1,Z),hide:(X,Z)=>S(X,!0,Z),toggle:(X,Z)=>S(X,!X.hidden,Z),add_class:(X,Z)=>X.classList.add(...Z.classes??[]),remove_class:(X,Z)=>X.classList.remove(...Z.classes??[]),toggle_class:(X,Z)=>(Z.classes??[]).forEach(($)=>X.classList.toggle($)),set_attr:(X,Z)=>{if(b(Z.name))X.setAttribute(Z.name,Z.value??"")},remove_attr:(X,Z)=>{if(b(Z.name))X.removeAttribute(Z.name)},toggle_attr:(X,Z)=>{if(!b(Z.name))return;if(X.hasAttribute(Z.name))X.removeAttribute(Z.name);else X.setAttribute(Z.name,"")},focus:(X)=>X.focus?.(),focus_first:(X)=>jZ(X)?.focus?.(),text:(X,Z)=>{let $=String(Z.value??"");if(X.textContent!==$)X.textContent=$},dispatch:(X,Z)=>{X.dispatchEvent(new CustomEvent(Z.name,{bubbles:!0,composed:!0,detail:Z.detail??{}}))},submit:(X)=>oX(X)?.requestSubmit?.()});function oX(X){if(X?.tagName==="FORM")return X;return X?.form??X?.closest?.("form")??null}function S(X,Z,$){if($?.transition)iX(X,$.transition,()=>X.hidden=Z);else X.hidden=Z}function b(X){if(!lX(X))return!0;return console.warn(`[phlex-reactive] refused client attr op on ${JSON.stringify(X)} — skipped`),!1}var GX=/^#[A-Za-z_][\w-]*$/;function sX(X){if(typeof X==="string"&&GX.test(X))return!0;return console.warn(`[phlex-reactive] refused cross-root mirror target ${JSON.stringify(X)} — skipped`),!1}function nX(X,Z){let $=X.getAttribute("data-reactive-show-equals");if($!==null)return Z===$;let Q=X.getAttribute("data-reactive-show-not");if(Q!==null)return Z!==Q;let j=X.getAttribute("data-reactive-show-in");if(j!==null){try{let z=JSON.parse(j);if(Array.isArray(z))return z.includes(Z)}catch{}return console.warn(`[phlex-reactive] malformed reactive_show in: list ${JSON.stringify(j)} — skipped`),null}for(let z of YX){let q=X.getAttribute(`data-reactive-show-${z}`);if(q!==null)return KX(z,q,Z)}return console.warn("[phlex-reactive] a reactive_show binding declares no predicate — skipped"),null}var YX=["gte","gt","lte","lt"],aX=["len_eq","len_gte","len_gt","len_lte","len_lt"];function rX(X,Z,$){if(!Number.isInteger(Z))return console.warn(`[phlex-reactive] reactive_show ${X}: needs an integer literal, got ${JSON.stringify(Z)} — skipped`),null;let Q=[...String($??"")].length;switch(X){case"len_eq":return Q===Z;case"len_gte":return Q>=Z;case"len_gt":return Q>Z;case"len_lte":return Q<=Z;case"len_lt":return Q<Z;default:return null}}function KX(X,Z,$){let Q=Number(Z);if(Number.isNaN(Q))return console.warn(`[phlex-reactive] reactive_show ${X}: needs a numeric literal, got ${JSON.stringify(Z)} — skipped`),null;let j=$==null?"":String($).trim(),z=j===""?NaN:Number(j);if(Number.isNaN(z))return!1;switch(X){case"gte":return z>=Q;case"gt":return z>Q;case"lte":return z<=Q;case"lt":return z<Q;default:return null}}function HX(X,Z){if(!X||typeof X!=="object")return null;if(typeof X.equals==="string")return Z===X.equals;if(typeof X.not==="string")return Z!==X.not;if(Array.isArray(X.in))return X.in.includes(Z);for(let $ of YX)if($ in X)return KX($,X[$],Z);for(let $ of aX)if($ in X)return rX($,X[$],Z);return null}var o="[data-reactive-show-field], [data-reactive-show]";function tX(X){try{let Z=JSON.parse(X);if(Z&&typeof Z==="object"&&!Array.isArray(Z))return Z}catch{}return console.warn(`[phlex-reactive] malformed compound reactive_show payload ${JSON.stringify(X)} — skipped`),null}function eX(X){try{let Z=JSON.parse(X);if(Array.isArray(Z)&&Z.every(($)=>$&&typeof $==="object"&&Array.isArray($.any)&&Array.isArray($.ops)))return Z}catch{}return console.warn(`[phlex-reactive] malformed reactive_on_complete payload ${JSON.stringify(X)} — skipped`),[]}function g(X,Z){if(!X||typeof X!=="object"||typeof X.field!=="string")return!1;let $=Z(X.field)??"";return HX(X,$)===!0}function P(X,Z){if(!Array.isArray(X)||X.length===0)return null;return X.some(($)=>Array.isArray($)&&$.length>0&&$.every((Q)=>g(Q,Z)))}function XZ(X){if(!Array.isArray(X)||X.length===0)return null;let Z=new Set;for(let $ of X){if(!Array.isArray($))continue;for(let Q of $)if(Q&&typeof Q==="object"&&typeof Q.field==="string")Z.add(Q.field)}return Z.size>0?[...Z]:null}function ZZ(X,Z){if(!X||typeof X!=="object")return null;let $=X.any;if(Array.isArray($)&&($.length===0||Array.isArray($[0])))return P($,Z);return $Z(X,Z)}function $Z(X,Z){let $=Array.isArray(X.all)?"all":Array.isArray(X.any)?"any":null;if(!$)return null;let Q=X[$];if(Q.length===0)return null;let j=Q.map((z)=>g(z,Z));return $==="all"?j.every(Boolean):j.some(Boolean)}function s(X){if(typeof X==="string"&&GX.test(X))return!0;return console.warn(`[phlex-reactive] refused cross-root show target ${JSON.stringify(X)} — skipped`),!1}var QZ='a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';function jZ(X){return X.querySelectorAll?.(QZ)?.[0]??null}function UX(X){if(Array.isArray(X))return X;if(typeof X!=="string")return[];try{let Z=JSON.parse(X);return Array.isArray(Z)?Z:[]}catch{return[]}}function zZ(X){if(X==null)return null;let Z=Array.isArray(X)?X:X.ops;if(Array.isArray(Z))return Z.length>0?Z:null;return console.warn("[phlex-reactive] $ops must be an ops chain or a [[op, args], ...] list — skipped"),null}function N(X,Z){for(let $ of X){if(!Array.isArray($))continue;let[Q,j={}]=$;if(!Object.hasOwn(i,Q)){console.warn(`[phlex-reactive] unknown client op ${JSON.stringify(Q)} — skipped`);continue}for(let z of Z(j))i[Q](z,j)}}function qZ(X,Z){let $=X.to;if(Z){if($==="@root")return[Z];if(typeof $!=="string"||$==="")return[];if(X.global)return[...document.querySelectorAll($)];return[...Z.querySelectorAll($)]}if(typeof $!=="string"||$===""||$==="@root")return[];return[...document.querySelectorAll($)]}class PZ extends WX{static values={token:String};#i;#x=new Map;#U=new Map;#RX;#E;#o;#k=0;#W=new Map;#S=new WeakMap;#P=new Map;#s=new WeakSet;#n=null;#J;#L;#V;#Z;#z;#b;#a;#w;#h;#Q;#B;#r=!1;#y=0;#t=!1;#j;#_;#M;#N;connect(){if(C=!0,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-defer-token"))this.#e(),this.#N=()=>this.#e(),this.element.addEventListener?.("turbo:morph-element",this.#N);if(this.#FX()){if(this.#J=()=>this.#u(),this.element.addEventListener?.("turbo:morph-element",this.#J),this.#u(),this.element.getAttribute?.("data-reactive-warn-unsaved")==="true")this.#tX()}if(this.#XZ())this.#Z=()=>this.#LX(),this.element.addEventListener?.("input",this.#Z),this.element.addEventListener?.("change",this.#Z),this.element.addEventListener?.("turbo:morph-element",this.#Z),this.#LX();if(this.#ZZ())this.#z=(X)=>this.#m(X),this.#b=()=>this.#m(null),this.element.addEventListener?.("input",this.#z),this.element.addEventListener?.("change",this.#z),this.element.addEventListener?.("turbo:morph-element",this.#b),this.#m(null);if(this.#C())this.#Q=(X)=>{if(X?.type==="input"&&!this.#KZ(X))return;this.#A()},this.element.addEventListener?.("input",this.#Q),this.element.addEventListener?.("turbo:morph-element",this.#Q),this.#A();if(this.#I())this.#B=()=>this.#d(),this.element.addEventListener?.("turbo:morph-element",this.#B),this.#d();if(this.#UZ())this.#j=(X)=>this.syncNestedJson(X),this.#_=()=>this.#D(),this.element.addEventListener?.("input",this.#j),this.element.addEventListener?.("change",this.#j),this.element.addEventListener?.("turbo:morph-element",this.#_),this.#D();if(this.#YZ())this.#M=()=>this.recompute(),this.element.addEventListener?.("turbo:morph-element",this.#M),this.recompute()}#FX(){if((this.element.getAttribute?.("data-action")??"").includes("reactive#trackDirty"))return!0;let X=this.element.querySelectorAll?.('[data-action*="reactive#trackDirty"]')??[];for(let Z of X)if(this.#$(Z))return!0;return!1}disconnect(){if(this.#mX(),this.#cX(),this.#eX(),this.#GZ(),this.#QZ(),this.#HZ(),this.#MZ(),this.#AZ(),this.#xZ(),this.#N)this.element.removeEventListener?.("turbo:morph-element",this.#N)}#e(){let X=this.element;if(!X?.id)return;let Z=X.getAttribute?.("data-reactive-defer-token");if(!Z)return;if(X.getAttribute?.("data-reactive-defer-pending")!=="true")return;F(X.id,Z)}dispatch(X){let{action:Z,params:$,debounce:Q,throttle:j,confirm:z,confirmWhen:q,outside:G,window:Y,optimistic:K}=X.params;if(!Z)return;let U=X.params.busy??this.#EZ(X.params.loading);if(G&&this.element.contains(X.target))return;let B=X.currentTarget??X.target;if(!Y&&!this.#OZ(K,B))X.preventDefault();let W=this.#p(z,q);if(!W)return this.#GX(B,Z,$,Q,j,K,U);Promise.resolve().then(()=>I(W,{el:B})).catch(()=>!1).then((V)=>{if(V)this.#GX(B,Z,$,Q,j,K,U)})}runOps(X){let{ops:Z,confirm:$,confirmWhen:Q,outside:j,window:z}=X.params,q=X.currentTarget??X.target;if(j&&this.element.contains(X.target))return;if(!z)X.preventDefault();let G=this.#p($,Q);if(!G)return this.#PX(this.#xX(Z));Promise.resolve().then(()=>I(G,{el:q})).catch(()=>!1).then((Y)=>{if(Y)this.#PX(this.#xX(Z))})}trackDirty(){this.#u()}recompute(X){if(X&&this.#s.has(X))return;let Z=this.#wX(),$=Z.map(([H])=>H),Q=this.element.getAttribute?.("data-reactive-scope")||null,j=(H)=>Q&&!H.includes("[")?`${Q}[${H}]`:H,z=this.#X(),q=new Map,G=(H)=>{if(q.has(H))return q.get(H);let J=null;for(let L of this.element.querySelectorAll(`[name="${j(H)}"]`))if(z(L)){J=L;break}return q.set(H,J),J};for(let H of $)this.#zX(H,G(H)?.value??"");let Y=this.element.getAttribute("data-reactive-compute-reducer-param"),K=Y?JX(Y):null;if(!K){this.#qX({},G);return}let U=this.#bX("data-reactive-compute-outputs-param"),B={};for(let[H,J]of Z){let L=G(H);if(J==="string")B[H]=L?.value??"";else{let A=Number(L?.value);B[H]=Number.isFinite(A)?A:0}}let W=K(B,{changed:this.#hX(X,$,Q)})||{},V=zZ(W.$ops),x=[];for(let H of U){if(H==="$ops"||!(H in W))continue;let J=G(H);if(!J)continue;if(String(W[H])===J.value)continue;J.value=W[H],x.push(J)}for(let H of Object.keys(W)){if(H==="$ops")continue;let J=W[H];if(J===void 0||J===null)continue;this.#zX(H,J)}this.#qX(W,G);for(let H of x){let J=new Event("input",{bubbles:!0});this.#s.add(J),H.dispatchEvent(J)}this.#CX(V,Boolean(X))}#CX(X,Z){let $=X===null?null:JSON.stringify(X),Q=$!==null&&$!==this.#n&&Z;if(this.#n=$,!Q)return;N(X,(j)=>this.#T(j.to==null?{...j,to:"@root"}:j))}listnavNext(X){this.#XX(X,1)}listnavPrev(X){this.#XX(X,-1)}listnavPick(X){let Z=this.#O(X),$=Z.findIndex((Q)=>Q.hasAttribute("data-reactive-highlighted"));if($<0)return;X.preventDefault(),Z[$].click()}listnavClose(X){for(let Z of this.#O(X))Z.removeAttribute("data-reactive-highlighted")}#XX(X,Z){let $=this.#O(X);if(!$.length)return;X.preventDefault();let Q=$.findIndex((q)=>q.hasAttribute("data-reactive-highlighted")),j=Q<0?Z>0?0:$.length-1:(Q+Z+$.length)%$.length;for(let q of $)q.removeAttribute("data-reactive-highlighted");let z=$[j];z.setAttribute("data-reactive-highlighted","true"),z.scrollIntoView?.({block:"nearest"})}#O(X){let $=(X?.currentTarget??X?.target??this.element).getAttribute?.("data-reactive-listnav-option-param")??this.element.getAttribute("data-reactive-listnav-option-param");if(!$)return[];let Q=this.#X();return Array.from(this.element.querySelectorAll($)).filter((j)=>!j.hidden&&Q(j))}tagsAdd(X){if(!this.#I())return;if(X?.defaultPrevented)return;if(this.#O(X).some((Q)=>Q.hasAttribute?.("data-reactive-highlighted")))return;X?.preventDefault?.();let Z=X?.currentTarget??X?.target;if(!Z)return;if(!this.#_X(String(Z.value??"").split(",")))return;if(Z.value="",this.#C())this.#A()}tagsPick(X){if(!this.#I())return;X?.preventDefault?.();let $=(X?.currentTarget??X?.target)?.getAttribute?.("data-reactive-tag-param");if(!$)return;if(!this.#_X([$]))return;let Q=this.#WZ();if(!Q)return;Q.value="",this.#A(),Q.focus?.()}tagsRemove(X){if(!this.#I())return;X?.preventDefault?.();let $=(X?.currentTarget??X?.target)?.getAttribute?.("data-reactive-tag-param");if(!$)return;let Q=this.#g();if(!Q)return;let j=this.#c(Q),z=j.filter((q)=>q.toLowerCase()!==$.toLowerCase());if(z.length===j.length)return;this.#MX(Q,z)}nestedAdd(X){X?.preventDefault?.();let Z=X?.currentTarget??X?.target,$=Z?.getAttribute?.("data-reactive-association-param");if(!$)return;if(typeof this.element?.querySelectorAll!=="function")return;let Q=this.#X(),j=[...this.element.querySelectorAll(`[data-reactive-nested-list="${$}"]`)].find(Q),q=[...this.element.querySelectorAll(`[data-reactive-nested-template="${$}"]`)].find(Q)?.content?.firstElementChild;if(!j||!q){this.#_Z($);return}let G=q.cloneNode(!0);this.#BZ(G,this.#VZ()),j.appendChild(G);let Y=Z?.getAttribute?.("data-reactive-nested-from-param"),K=Z?.getAttribute?.("data-reactive-nested-clear-param")==="true",U=this.#IX(G,Y,K);if(Y)U?.focus?.();else[...G.querySelectorAll?.("input, select, textarea")??[]][0]?.focus?.();if(j.getAttribute?.("data-reactive-nested-json")===$)this.#$X($)}#IX(X,Z,$){if(!Z)return null;let Q;try{Q=JSON.parse(Z)}catch{return null}if(!Q||typeof Q!=="object")return null;let j=this.#X(),z=[...X.querySelectorAll?.("input, select, textarea")??[]],q=[];for(let[G,Y]of Object.entries(Q)){let K=[...this.element.querySelectorAll?.(Y)??[]].find(j);if(!K)continue;let U=z.find((B)=>this.#jX(B.getAttribute?.("name"))===G);if(!U)continue;this.#TX(U,K),q.push(K)}if($)for(let G of q)this.#EX(G);return q[0]??null}#TX(X,Z){if(X.type==="checkbox")X.checked=Z.type==="checkbox"?!!Z.checked:this.#v(Z)!=="";else X.value=this.#v(Z);if(typeof X.dispatchEvent==="function")X.dispatchEvent(new Event("input",{bubbles:!0}))}#EX(X){if(X.type==="checkbox")X.checked=!1;else X.value="";if(typeof X.dispatchEvent==="function")X.dispatchEvent(new Event("input",{bubbles:!0}))}nestedRemove(X){X?.preventDefault?.();let Z=X?.currentTarget??X?.target,$=Z?.closest?.("[data-reactive-nested-row]");if(!$)return;if($.closest?.('[data-controller~="reactive"]')!==this.element)return;let Q=Z?.getAttribute?.("data-reactive-confirm-param"),j=Z?.getAttribute?.("data-reactive-confirm-when-param"),z=this.#p(Q,j);if(!z)return this.#ZX($);let q=this.#QX($),G=this.#kX(z,q);return Promise.resolve().then(()=>I(G,{el:Z,row:$,fields:q})).catch(()=>!1).then((Y)=>{if(Y)this.#ZX($)})}#kX(X,Z){if(!X.includes("%{"))return X;return X.replace(/%\{(\w+)\}/g,($,Q)=>Object.prototype.hasOwnProperty.call(Z,Q)?Z[Q]:$)}#ZX(X){let Z=[...X.querySelectorAll?.('input[name$="[_destroy]"]')??[]][0];if(Z){if(Z.value="1",typeof Z.dispatchEvent==="function")Z.dispatchEvent(new Event("input",{bubbles:!0}));X.hidden=!0}else X.parentNode?.removeChild?.(X);this.#D()}syncNestedJson(X){let Z=X?.target;if(!Z||!this.#$(Z))return;this.#D()}#D(){if(typeof this.element?.querySelectorAll!=="function")return;let X=this.#X();for(let Z of[...this.element.querySelectorAll("[data-reactive-nested-json]")].filter(X))this.#$X(Z.getAttribute("data-reactive-nested-json"))}#$X(X){let Z=this.#X(),$=[...this.element.querySelectorAll(`[data-reactive-nested-list="${X}"]`)].find(Z);if(!$)return;let Q=this.#SX($);if(!Q)return;let j=[];for(let q of[...$.querySelectorAll?.("[data-reactive-nested-row]")??[]]){if(!Z(q)||q.hidden)continue;j.push(this.#QX(q))}let z=JSON.stringify(j);if(Q.value===z)return;if(Q.value=z,typeof Q.dispatchEvent==="function")Q.dispatchEvent(new Event("input",{bubbles:!0}))}#SX(X){let Z=X.getAttribute?.("data-reactive-nested-json-field");if(!Z)return null;let $=this.#X();return[...this.element.querySelectorAll(Z)].find($)??null}#QX(X){let Z={};for(let $ of[...X.querySelectorAll?.("input, select, textarea")??[]]){let Q=this.#jX($.getAttribute?.("name"));if(Q===null||Q==="_destroy")continue;Z[Q]=this.#v($)}return Z}#jX(X){if(!X)return null;let Z=X.match(/\[([^\][]+)\]$/);return Z?Z[1]:X}#v(X){if(X.type==="checkbox")return X.checked?X.value||"on":"";return X.value??""}#bX(X){let Z=this.element.getAttribute(X);if(!Z)return[];try{let $=JSON.parse(Z);return Array.isArray($)?$:[]}catch{return[]}}#wX(){let X=this.element.getAttribute("data-reactive-compute-inputs-param");if(!X)return[];try{let Z=JSON.parse(X);if(Array.isArray(Z))return Z.map(($)=>[$,"number"]);if(Z&&typeof Z==="object")return Object.entries(Z);return[]}catch{return[]}}#hX(X,Z,$){let Q=X?.target;if(!Q?.name||typeof Q.closest!=="function")return null;let j=this.#yX(Q.name,$);if(!Z.includes(j))return null;return this.#$(Q)?j:null}#yX(X,Z){if(!Z)return X;let $=`${Z}[`;return X.startsWith($)&&X.endsWith("]")?X.slice($.length,-1):X}#zX(X,Z){let $=String(Z);for(let Q of this.#vX(X)){if(Q.textContent===$)continue;Q.textContent=$}}#vX(X){let Z=this.element.querySelectorAll(`[data-reactive-text="${X}"]`);return Array.from(Z).filter(($)=>this.#$($))}#qX(X,Z){let $=this.#fX();for(let[Q,j]of Object.entries($)){let z=Q in X?X[Q]:Z(Q)?.value;if(z===void 0||z===null)continue;let q=String(z);for(let G of Array.isArray(j)?j:[j]){if(!sX(G))continue;for(let Y of document.querySelectorAll(G)){if(Y.textContent===q)continue;Y.textContent=q}}}}#fX(){let X=this.element.getAttribute("data-reactive-compute-mirror-param");if(!X)return{};try{let Z=JSON.parse(X);return Z&&typeof Z==="object"&&!Array.isArray(Z)?Z:{}}catch{return{}}}#GX(X,Z,$,Q,j,z,q){if(this.#F("reactive:before-dispatch",{action:Z,params:this.#AX($),element:this.element},{cancelable:!0}).defaultPrevented)return;let Y=Number(Q)||0;if(Y>0)return this.#uX(X,Y,Z,$,z,q);let K=Number(j)||0;if(K>0)return this.#gX(X,K,Z,$,z,q);return this.#R(Z,$,z,X,q)}#R(X,Z,$,Q,j){let z=this.#DZ($,Q),q=this.#RZ(X,Q,j),G=this.#YX()?this.#pX($,Q):null;return this.queue=(this.queue??Promise.resolve()).then(()=>this.#nX(X,Z,z,q,G)),this.queue}#pX(X,Z){if(!X?.hide)return null;let $=this.#OX(X,Z);if(!$.length)return null;return()=>{let Q=$.filter((j)=>j.isConnected&&!j.hidden);if(!Q.length)return;console.warn("[phlex-reactive] optimistic: { hide: true } was undone by the reply's re-render — "+"the element is visible again. For an instant delete, return reply.remove so the server removes it; otherwise the hide only flashes.",Q)}}#uX(X,Z,$,Q,j,z){this.#f(X);let q=()=>{this.#f(X),this.#R($,Q,j,X,z)},G=setTimeout(q,Z);X?.addEventListener?.("blur",q,{once:!0}),this.#x.set(X,{timer:G,flush:q})}#f(X){let Z=this.#x.get(X);if(!Z)return;clearTimeout(Z.timer),X?.removeEventListener?.("blur",Z.flush),this.#x.delete(X)}#mX(){for(let X of[...this.#x.keys()])this.#f(X)}#gX(X,Z,$,Q,j,z){let q=this.#U.get(X)??new Map;if(q.has($))return;let G=setTimeout(()=>{if(q.delete($),q.size===0)this.#U.delete(X)},Z);return q.set($,G),this.#U.set(X,q),this.#R($,Q,j,X,z)}#cX(){for(let X of this.#U.values())for(let Z of X.values())clearTimeout(Z);this.#U.clear()}#F(X,Z,{cancelable:$=!1}={}){let Q=new CustomEvent(X,{bubbles:!0,composed:!0,cancelable:$,detail:Z});return(this.element.isConnected?this.element:document).dispatchEvent(Q),Q}#q(X,Z,$,Q){let j=()=>{if(!this.element.isConnected){console.warn("[phlex-reactive] retry() ignored — the reactive root left the DOM");return}return this.#R(X,Z)};this.#F("reactive:error",{action:X,params:$,...Q,retry:j})}#G(X){if(this.element?.isConnected===!1)return;this.element?.setAttribute?.("data-reactive-error",X)}#dX(){this.element?.removeAttribute?.("data-reactive-error")}#lX(){let X=document.querySelector("[data-reactive-error-flash]");if(!X?.content)return;let Z=X.getAttribute("data-reactive-error-flash")||"flash",$=document.getElementById(Z);if(!$)return;$.appendChild(X.content.cloneNode(!0))}#iX(){if(typeof sessionStorage>"u")return Promise.resolve();let X=Number(sessionStorage.getItem(m));if(!Number.isFinite(X)||X<=0)return Promise.resolve();if(!D)D=!0,console.warn(`[phlex-reactive] latency simulator ACTIVE — every action is delayed by ${X}ms. Call PhlexReactive.disableLatencySim() (or clear sessionStorage) to turn it off.`);return new Promise((Z)=>setTimeout(Z,X))}#YX(){return this.element?.getAttribute?.("data-reactive-debug")==="true"}#KX(){return typeof performance<"u"&&typeof performance.now==="function"?performance.now():Date.now()}#oX(X){if(!X)return[];let Z=[],$=/<turbo-stream\b([^>]*)>/g,Q;while((Q=$.exec(X))!==null){let j=Q[1],z=j.match(/\baction="([^"]*)"/)?.[1]??"?",q=j.match(/\btarget="([^"]*)"/)?.[1];Z.push(q?`${z} → #${q}`:z)}return Z}#sX(X){let{action:Z,status:$,ms:Q}=X,z=`reactive ${this.element?.id?`#${this.element.id} `:""}${Z} → ${$??"—"} (${Math.round(Q)}ms)`;if(console.groupCollapsed(z),console.log(`params: [${X.paramNames.join(", ")}] + collected: [${X.fieldNames.join(", ")}]`),console.log(`encoding: ${X.encoding}`),X.streams.length)console.log(`streams: ${X.streams.join(" ")}`);console.log(`token: ${X.tokenRefreshed?"refreshed ✓":"unchanged"}`),console.groupEnd()}async#nX(X,Z,$,Q,j){let{fields:z,files:q}=this.#WX(),G=this.#AX(Z),Y={...z,...G},K=this.#Y,U=q.length>0,B=U?this.#PZ(K,X,Y,q):JSON.stringify({token:K,act:X,params:Y}),W=this.#YX()?{action:X,paramNames:Object.keys(G),fieldNames:Object.keys(z),encoding:U?"multipart":"json",status:null,streams:[],tokenRefreshed:!1,started:this.#KX()}:null;await this.#iX();try{if(navigator.onLine===!1){this.#K($),this.#G("offline"),this.#q(X,Z,Y,{kind:"offline"});return}let V;try{let L={Accept:"text/vnd.turbo-stream.html","X-CSRF-Token":this.#bZ()};if(!U)L["Content-Type"]="application/json";let A=this.#wZ();if(A)L["X-Pgbus-Connection"]=A;V=await fetch(this.#kZ(),{method:"POST",headers:L,body:B,credentials:"same-origin",signal:AbortSignal.timeout(this.#SZ())})}catch(L){if(console.error("[phlex-reactive] action error",L),this.#K($),L?.name==="TimeoutError"||L?.name==="AbortError"){this.#G("timeout"),this.#q(X,Z,Y,{kind:"timeout"});return}this.#lX(),this.#G("network"),this.#q(X,Z,Y,{kind:"network"});return}if(W)W.status=V.status;if(V.redirected){console.error("[phlex-reactive] action was redirected (auth/CSRF?) — no update applied"),this.#K($),this.#G("redirected"),this.#q(X,Z,Y,{kind:"redirected",status:V.status});return}if(!V.ok){let L=await V.text();if(console.error(`[phlex-reactive] action failed: HTTP ${V.status}`,L),this.#K($),(V.headers.get("Content-Type")||"").includes("turbo-stream")){let A=this.#UX(L);if(this.#Y=A??this.#Y,W)this.#HX(W,L,A);window.Turbo.renderStreamMessage(L)}this.#G("http"),this.#q(X,Z,Y,{kind:"http",status:V.status,body:L});return}let x=V.headers.get("Content-Type")||"";if(!x.includes("turbo-stream")){console.error(`[phlex-reactive] expected a turbo-stream, got "${x}" — no update applied`),this.#K($),this.#G("content-type"),this.#q(X,Z,Y,{kind:"content-type",status:V.status});return}let H=await V.text(),J=this.#UX(H);if(this.#Y=J??this.#Y,W)this.#HX(W,H,J);if(window.Turbo.renderStreamMessage(H),j)queueMicrotask(j);this.#dX(),this.#F("reactive:applied",{action:X,params:Y,html:H})}catch(V){console.error("[phlex-reactive] action error",V),this.#K($),this.#F("reactive:error",{action:X,params:Y,kind:"apply"})}finally{if(Q?.(),W)this.#sX({...W,ms:this.#KX()-W.started})}}#HX(X,Z,$){X.streams=this.#oX(Z),X.tokenRefreshed=$!=null}get#Y(){return this.#i??this.tokenValue}set#Y(X){this.#i=X}#UX(X){let Z=this.element.id;if(!Z)return X.match(/data-reactive-token-value="([^"]+)"/)?.[1];let{token:$,self:Q}=this.#aX(Z),j=X.match($);if(j)return j[1];let z=X.match(Q);if(z)return z[1].match(/data-reactive-token-value="([^"]+)"/)?.[1];return}#aX(X){let Z=this.#o;if(Z&&Z.id===X)return Z;let $=gX(X);return this.#o={id:X,token:new RegExp(`<turbo-stream\\b[^>]*\\baction="reactive:token"[^>]*\\btarget="${$}"[^>]*\\bdata-reactive-token-value="([^"]+)"`),self:new RegExp(`<turbo-stream\\b[^>]*\\baction="(?:replace|update)"[^>]*\\btarget="${$}"[^>]*>([\\s\\S]*?)</turbo-stream>`)}}#$(X){return X.closest('[data-controller~="reactive"]')===this.element}#X(){if(this.element.querySelectorAll('[data-controller~="reactive"]').length===0)return()=>!0;return(Z)=>this.#$(Z)}#p(X,Z){if(X)return X;if(!Z)return null;let $=Z;if(typeof Z==="string")try{$=JSON.parse(Z)}catch{return console.warn(`[phlex-reactive] malformed conditional confirm payload ${JSON.stringify(Z)} — skipped`),null}if(!$||typeof $!=="object")return null;let{fields:Q}=this.#WX(),j=(q)=>Q[q],z;if(typeof $.predicate==="string"){let q=LX($.predicate);if(!q)return console.warn(`[phlex-reactive] confirm predicate "${$.predicate}" is not registered — proceeding without a dialog (register it with setConfirmPredicate)`),null;z=!!q(Q)}else z=P($.groups?.any,j)===!0;return z?$.message:null}#WX(){let X={},Z=[],$=this.#X();return this.element.querySelectorAll("input[name], select[name], textarea[name]").forEach((Q)=>{if(!$(Q))return;if(Q.type==="file")for(let j of Q.files??[])Z.push({name:Q.name,file:j,multiple:Q.multiple});else if(Q.type==="checkbox")X[Q.name]=Q.checked;else if(Q.type==="radio"){if(Q.checked)X[Q.name]=Q.value}else X[Q.name]=Q.value}),this.element.querySelectorAll("[name]:is(lexxy-editor, trix-editor, [contenteditable=''], [contenteditable=true], [contenteditable=plaintext-only])").forEach((Q)=>{if(!$(Q))return;let j=Q.getAttribute("name");if(!j)return;let z=X[j];if(z==null||z==="")X[j]=Q.value??Q.textContent??Q.innerHTML??""}),{fields:X,files:Z}}#u(){if(typeof this.element?.querySelectorAll!=="function")return;let X=0;if(this.element.querySelectorAll("input[name], select[name], textarea[name]").forEach((Z)=>{if(!this.#$(Z))return;if(Z.type==="file")return;if(this.#rX(Z))Z.setAttribute("data-reactive-dirty","true"),X++;else Z.removeAttribute("data-reactive-dirty")}),X>0)this.element.setAttribute("data-reactive-dirty",String(X));else this.element.removeAttribute("data-reactive-dirty")}#rX(X){if(X.type==="checkbox"||X.type==="radio")return X.checked!==X.defaultChecked;if(X.tag==="select"||X.options)return Array.from(X.options??[]).some((Z)=>Z.selected!==Z.defaultSelected);return X.value!==X.defaultValue}#JX(){let X=this.element.getAttribute?.("data-reactive-dirty"),Z=Number(X);return Number.isFinite(Z)&&Z>0?Z:0}#tX(){if(typeof window>"u"||typeof window.addEventListener!=="function")return;this.#L=(X)=>{if(this.#JX()===0)return;return X.preventDefault(),X.returnValue="You have unsaved changes.",X.returnValue},this.#V=(X)=>{if(this.#JX()===0)return;if(!(typeof window.confirm==="function"?window.confirm("You have unsaved changes. Leave anyway?"):!0))X.preventDefault?.()},window.addEventListener("beforeunload",this.#L),window.addEventListener("turbo:before-visit",this.#V)}#eX(){if(this.#J)this.element.removeEventListener?.("turbo:morph-element",this.#J),this.#J=void 0;if(typeof window<"u"&&typeof window.removeEventListener==="function"){if(this.#L)window.removeEventListener("beforeunload",this.#L);if(this.#V)window.removeEventListener("turbo:before-visit",this.#V)}this.#L=void 0,this.#V=void 0}#XZ(){if(this.element.getAttribute?.("data-reactive-show-targets"))return!0;let X=this.element.querySelectorAll?.(o)??[];for(let Z of X)if(this.#$(Z))return!0;return!1}#ZZ(){return!!this.element.getAttribute?.("data-reactive-on-complete")}#$Z(){let X=this.element.getAttribute?.("data-reactive-on-complete")??null;if(X!==this.#a)this.#a=X,this.#w=X==null?[]:eX(X),this.#h=this.#w.map(()=>!1);return this.#w}#m(X){let Z=this.#$Z();if(!Z.length)return;let $=this.#X(),Q=this.element.getAttribute?.("data-reactive-scope")||null,j=new Map,z=(q)=>{if(!j.has(q))j.set(q,this.#BX(q,$,Q));return j.get(q)};Z.forEach((q,G)=>{let Y=P(q.any,z);if(Y===null)return;let K=Y&&!this.#h[G]&&Boolean(X);if(this.#h[G]=Y,K)N(q.ops,(U)=>this.#T(U.to==null?{...U,to:"@root"}:U))})}#QZ(){if(!this.#z)return;this.element.removeEventListener?.("input",this.#z),this.element.removeEventListener?.("change",this.#z),this.element.removeEventListener?.("turbo:morph-element",this.#b)}#LX(){if(typeof this.element?.querySelectorAll!=="function")return;let X=this.#X(),Z=this.element.getAttribute?.("data-reactive-scope")||null,$=new Map,Q=(j)=>{if(!$.has(j))$.set(j,this.#BX(j,X,Z));return $.get(j)};for(let j of this.element.querySelectorAll(o)){if(!X(j))continue;let z=j.getAttribute("data-reactive-show");if(z!==null){let K=ZZ(tX(z),Q);if(K!==null)this.#VX(j,K,X,Z);continue}let q=j.getAttribute("data-reactive-show-field");if(!q)continue;let G=Q(q);if(G===null)continue;let Y=nX(j,G);if(Y===null)continue;this.#VX(j,Y,X,Z)}this.#jZ(Q)}#VX(X,Z,$,Q){if(X.hidden=!Z,X.getAttribute("data-reactive-show-disable")!=="true")return;if(typeof X.querySelectorAll!=="function")return;for(let j of X.querySelectorAll("input[name], select[name], textarea[name]"))if($(j))j.disabled=!Z;if(X.name&&$(X))X.disabled=!Z}#jZ(X){let Z=this.#qZ();for(let[$,Q]of Object.entries(Z)){if($.startsWith("#")){this.#zZ($,Q,X);continue}if(!Q||typeof Q!=="object"||Array.isArray(Q))continue;let j=X($);if(j===null)continue;let z=()=>j;for(let[q,G]of Object.entries(Q)){if(!s(q))continue;let Y;if(Array.isArray(G)){if(G.length===0){console.warn(`[phlex-reactive] malformed reactive_show_targets group for ${q} — skipped`);continue}Y=G.every((K)=>g(K,z))}else{let K=HX(G,j);if(K===null){console.warn(`[phlex-reactive] malformed reactive_show_targets predicate for ${q} — skipped`);continue}Y=K}for(let K of document.querySelectorAll(q))K.hidden=!Y}}}#zZ(X,Z,$){if(!s(X))return;let Q=Z&&typeof Z==="object"&&!Array.isArray(Z)?Z.any:null,j=XZ(Q);if(j===null){console.warn(`[phlex-reactive] malformed reactive_show_targets conditions for ${X} — skipped`);return}if(j.every((q)=>$(q)===null))return;let z=P(Q,$);if(z===null)return;for(let q of document.querySelectorAll(X))q.hidden=!z}#qZ(){let X=this.element.getAttribute?.("data-reactive-show-targets");if(!X)return{};try{let Z=JSON.parse(X);if(Z&&typeof Z==="object"&&!Array.isArray(Z))return Z}catch{}return console.warn("[phlex-reactive] malformed data-reactive-show-targets — ignored. "+"Did two reactive_show_targets calls collide on one root? Declare every field in ONE call: reactive_show_targets(mode: { ... }, kind: { ... })"),{}}#BX(X,Z,$){let Q=$&&!X.includes("[")?`${$}[${X}]`:X,j=!1,z=null;for(let q of this.element.querySelectorAll(`[name="${Q}"]`)){if(!Z(q))continue;if(q.type==="checkbox")return q.checked?"true":"false";if(q.type==="radio"){if(q.checked)return q.value??"";j=!0;continue}z??=q}if(z)return z.value??"";return j?"":null}#GZ(){if(!this.#Z)return;this.element.removeEventListener?.("input",this.#Z),this.element.removeEventListener?.("change",this.#Z),this.element.removeEventListener?.("turbo:morph-element",this.#Z),this.#Z=void 0}#C(){return!!(this.element.getAttribute?.("data-reactive-filter-input")&&this.element.getAttribute?.("data-reactive-filter-option"))}#YZ(){return this.element.getAttribute?.("data-reactive-compute-seed")==="true"}#KZ(X){let Z=this.element.getAttribute("data-reactive-filter-input");return!!Z&&typeof X.target?.matches==="function"&&X.target.matches(Z)}#A(){if(typeof this.element?.querySelectorAll!=="function")return;let X=this.element.getAttribute("data-reactive-filter-input"),Z=this.element.getAttribute("data-reactive-filter-option");if(!X||!Z)return;let $=this.#X(),Q=[...this.element.querySelectorAll(X)].find($);if(!Q)return;let j=(Q.value??"").trim().toLowerCase(),z=0;for(let Y of this.element.querySelectorAll(Z)){if(!$(Y))continue;let K=(Y.getAttribute("data-reactive-filter-text")??Y.textContent??"").toLowerCase(),U=Y.hasAttribute?.("data-reactive-tags-selected")||j!==""&&!K.includes(j);if(Y.hidden=U,U)Y.removeAttribute("data-reactive-highlighted");else z++}let q=this.element.getAttribute("data-reactive-filter-group");if(q)for(let Y of this.element.querySelectorAll(q)){if(!$(Y))continue;let K=[...Y.querySelectorAll(Z)].filter($);if(K.length===0)continue;Y.hidden=K.every((U)=>U.hidden)}let G=this.element.getAttribute("data-reactive-filter-empty");if(G){for(let Y of this.element.querySelectorAll(G))if($(Y))Y.hidden=z>0}}#HZ(){if(!this.#Q)return;this.element.removeEventListener?.("input",this.#Q),this.element.removeEventListener?.("turbo:morph-element",this.#Q),this.#Q=void 0}#I(){return!!this.element.getAttribute?.("data-reactive-tags-field")}#UZ(){if(typeof this.element?.querySelector!=="function")return!1;return!!this.element.querySelector("[data-reactive-nested-json]")}#g(){if(typeof this.element?.querySelectorAll!=="function")return null;let X=this.element.getAttribute("data-reactive-tags-field");if(!X)return null;let Z=this.#X();return[...this.element.querySelectorAll(X)].find(Z)??null}#c(X){let Z=new Set,$=[];for(let Q of String(X.value??"").split(",")){let j=Q.trim();if(j===""||Z.has(j.toLowerCase()))continue;Z.add(j.toLowerCase()),$.push(j)}return $}#_X(X){let Z=this.#g();if(!Z)return!1;let $=this.#c(Z),Q=new Set($.map((z)=>z.toLowerCase())),j=!1;for(let z of X){let q=String(z??"").trim();if(q===""||Q.has(q.toLowerCase()))continue;Q.add(q.toLowerCase()),$.push(q),j=!0}if(j)this.#MX(Z,$);return j}#MX(X,Z){if(X.value=Z.join(","),typeof X.dispatchEvent==="function")X.dispatchEvent(new Event("input",{bubbles:!0}));this.#d()}#WZ(){if(typeof this.element?.querySelectorAll!=="function")return null;let X=this.element.getAttribute("data-reactive-filter-input");if(!X)return null;let Z=this.#X();return[...this.element.querySelectorAll(X)].find(Z)??null}#d(){let X=this.#g();if(!X)return;let Z=this.#c(X),$=this.#X();this.#JZ(Z,$),this.#LZ(Z,$)}#JZ(X,Z){let $=[...this.element.querySelectorAll("[data-reactive-tags-list]")].find(Z);if(!$)return;let j=[...this.element.querySelectorAll("[data-reactive-tags-template]")].find(Z)?.content?.firstElementChild;if(!j){if(!this.#r)console.warn("[phlex-reactive] reactive_tags: no chip <template data-reactive-tags-template> found in this root — "+"chips will not render (the hidden field still updates). Add a template with a [data-reactive-tag-text] node and a reactive_tags_remove button."),this.#r=!0;return}while($.firstChild)$.removeChild($.firstChild);for(let z of X){let q=j.cloneNode(!0);q.setAttribute?.("data-reactive-tag",z);let G=q.matches?.("[data-reactive-tag-text]")?q:(q.querySelectorAll?.("[data-reactive-tag-text]")??[])[0];if(G)G.textContent=z;let Y=[...q.querySelectorAll?.('[data-action*="reactive#tagsRemove"]')??[]];if(q.matches?.('[data-action*="reactive#tagsRemove"]'))Y.push(q);for(let K of Y)K.setAttribute?.("data-reactive-tag-param",z);$.appendChild(q)}}#LZ(X,Z){let $=new Set(X.map((Q)=>Q.toLowerCase()));for(let Q of this.element.querySelectorAll("[role=option]")){if(!Z(Q))continue;let j=Q.getAttribute?.("data-reactive-tag-param");if(!j)continue;if($.has(j.toLowerCase()))Q.setAttribute("data-reactive-tags-selected","true"),Q.hidden=!0,Q.removeAttribute?.("data-reactive-highlighted");else if(Q.hasAttribute?.("data-reactive-tags-selected")){if(Q.removeAttribute("data-reactive-tags-selected"),!this.#C())Q.hidden=!1}}if(this.#C())this.#A()}#VZ(){return this.#y=Math.max(this.#y+1,Date.now()),this.#y}#BZ(X,Z){let $=[X,...X.querySelectorAll?.("*")??[]];for(let Q of $)for(let j of["name","id","for"]){let z=Q.getAttribute?.(j);if(z&&z.includes("NEW_ROW"))Q.setAttribute?.(j,z.replaceAll("NEW_ROW",String(Z)))}}#_Z(X){if(this.#t)return;console.warn(`[phlex-reactive] nested rows: no owned [data-reactive-nested-list="${X}"] container + <template data-reactive-nested-template="${X}"> pair found in this root — the add `+"trigger did nothing. Render both inside the same reactive root (reactive_nested_list / reactive_nested_template)."),this.#t=!0}#MZ(){if(!this.#B)return;this.element.removeEventListener?.("turbo:morph-element",this.#B),this.#B=void 0}#AZ(){if(this.#j)this.element.removeEventListener?.("input",this.#j),this.element.removeEventListener?.("change",this.#j),this.#j=void 0;if(this.#_)this.element.removeEventListener?.("turbo:morph-element",this.#_),this.#_=void 0}#xZ(){if(!this.#M)return;this.element.removeEventListener?.("turbo:morph-element",this.#M),this.#M=void 0}#PZ(X,Z,$,Q){let j=new FormData;j.append("token",X),j.append("act",Z);for(let[q,G]of Object.entries($))this.#l(j,`params[${q}]`,G);let z=this.#NZ(Q);for(let{name:q,file:G,multiple:Y}of Q){let U=Y||z.has(q)?`params[${q}][]`:`params[${q}]`;j.append(U,G,G.name)}return j}#l(X,Z,$){if($==null)X.append(Z,"");else if(Array.isArray($))$.forEach((Q,j)=>this.#l(X,`${Z}[${j}]`,Q));else if(typeof $==="object")for(let[Q,j]of Object.entries($))this.#l(X,`${Z}[${Q}]`,j);else X.append(Z,String($))}#NZ(X){let Z=new Map;for(let{name:$}of X)Z.set($,(Z.get($)??0)+1);return new Set([...Z].filter(([,$])=>$>1).map(([$])=>$))}#AX(X){if(!X)return{};try{return typeof X==="string"?JSON.parse(X):X}catch{return{}}}#xX(X){return UX(X)}#PX(X){N(X,(Z)=>this.#T(Z))}#T(X){let Z=X.to;if(Z==="@root")return[this.element];if(typeof Z!=="string"||Z==="")return[];if(X.global)return[...document.querySelectorAll(Z)];return[...this.element.querySelectorAll(Z)].filter(($)=>this.#$($))}#OZ(X,Z){if(X?.checked!=="keep")return!1;let $=Z?.type;return $==="checkbox"||$==="radio"}#DZ(X,Z){if(!X)return null;let $=this.#NX(X,Z,!0);return $.length?$:null}#K(X){if(!X)return;if(!this.element.isConnected)return;for(let Z of X)Z()}#RZ(X,Z,$){this.#CZ(X,Z);let Q=$?this.#NX($,Z,!1):[];jX();let j=!1;return()=>{if(j)return;j=!0,this.#IZ(X,Z),zX();for(let z of Q)z()}}#NX(X,Z,$){let Q=[];for(let j of this.#OX(X,Z)){if(X.add_class){let z=X.add_class.filter((q)=>!j.classList.contains(q));if(j.classList.add(...z),z.length)Q.push(()=>j.classList.remove(...z))}if(X.remove_class){let z=X.remove_class.filter((q)=>j.classList.contains(q));if(j.classList.remove(...z),z.length)Q.push(()=>j.classList.add(...z))}if(X.toggle_class)X.toggle_class.forEach((z)=>j.classList.toggle(z)),Q.push(()=>X.toggle_class.forEach((z)=>j.classList.toggle(z)));if(X.hide)j.hidden=!0,Q.push(()=>j.hidden=!1);if(X.show)j.hidden=!1,Q.push(()=>j.hidden=!0)}if(Z&&(X.disable||X.text!=null))Q.push(this.#FZ(X,Z));if($&&X.checked==="keep"&&Z&&"checked"in Z){let j=Z.checked;Q.push(()=>Z.checked=!j)}return Q}#OX(X,Z){if(X.to==null)return Z?[Z]:[];return this.#T({to:X.to})}#FZ(X,Z){let $=this.#P.get(Z);if($)$.count++;else this.#P.set(Z,{count:1,disabled:Z.disabled,html:Z.innerHTML,hadText:X.text!=null,swappedTo:X.text});if(X.disable)Z.disabled=!0;if(X.text!=null)Z.innerHTML=X.text;return()=>this.#TZ(Z,X)}#CZ(X,Z){if(this.#H(Z,X,1),this.#H(this.element,X,1),this.#W.set(X,(this.#W.get(X)??0)+1),this.#k++===0)this.element.setAttribute("aria-busy","true");for(let $ of this.#DX(X))this.#H($,X,1)}#IZ(X,Z){this.#H(Z,X,-1),this.#H(this.element,X,-1);let $=(this.#W.get(X)??1)-1;if($<=0)this.#W.delete(X);else this.#W.set(X,$);if(--this.#k<=0)this.#k=0,this.element.removeAttribute("aria-busy");for(let Q of this.#DX(X))this.#H(Q,X,-1)}#H(X,Z,$){if(!X||typeof X.getAttribute!=="function")return;let Q=this.#S.get(X)??new Map,j=(Q.get(Z)??0)+$;if(j<=0)Q.delete(Z);else Q.set(Z,j);if(Q.size===0){this.#S.delete(X),X.removeAttribute("data-reactive-busy");return}this.#S.set(X,Q),X.setAttribute("data-reactive-busy",[...Q.keys()].join(" "))}#DX(X){return[...this.element.querySelectorAll?.("[data-reactive-busy-on]")??[]].filter(($)=>$.getAttribute("data-reactive-busy-on")===X&&this.#$($))}#TZ(X,Z){let $=this.#P.get(X);if(!$)return;if(--$.count>0)return;if(this.#P.delete(X),!X.isConnected)return;if(Z.disable)X.disabled=$.disabled;if($.hadText&&X.innerHTML===$.swappedTo)X.innerHTML=$.html}#EZ(X){if(!X||typeof X!=="object")return null;let{class:Z,...$}=X;return Z==null?X:{...$,add_class:Z}}#kZ(){return this.#RX??=document.querySelector('meta[name="phlex-reactive-action-path"]')?.content||"/reactive/actions"}#SZ(){if(this.#E!=null)return this.#E;let X=document.querySelector('meta[name="phlex-reactive-timeout"]')?.content,Z=Number(X);return this.#E=Number.isFinite(Z)&&Z>0?Z:30000}#bZ(){return document.querySelector('meta[name="csrf-token"]')?.content??""}#wZ(){return document.querySelector("pgbus-stream-source[connection-id]")?.getAttribute("connection-id")||document.querySelector('meta[name="pgbus-connection-id"]')?.content||null}}export{UZ as resetReactiveDefers,MZ as resetReactiveActivity,VX as registerReactiveVisit,BX as registerReactiveToken,fX as registerReactiveOffline,_X as registerReactiveJs,IX as registerReactiveEffects,RX as registerReactiveDismiss,MX as registerReactiveDefer,l as registerReactiveActions,_Z as reactiveActivityCount,WZ as pendingDeferVia,zX as exitReactiveActivity,gX as escapeRegExp,jX as enterReactiveActivity,pX as enableLatencySim,uX as disableLatencySim,PZ as default,cX as checkReactiveRegistration,AZ as __resetReactiveRegistrationForTest,VZ as __resetReactiveOfflineForTest,BZ as __resetReactiveLatencyForTest,LZ as __resetReactiveEffectsForTest,JZ as __resetReactiveDismissForTest,xZ as __markReactiveConnectedForTest,m as LATENCY_KEY,QX as ACTIVE_ATTR};
|
|
2
2
|
|
|
3
|
-
//# debugId=
|
|
3
|
+
//# debugId=B44B95BDB680993364756E2164756E21
|
|
4
4
|
//# sourceMappingURL=reactive_controller.min.js.map
|