stimeo-ui 0.16.0 → 0.18.0
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 +136 -0
- data/README.md +27 -11
- data/dist/cable/index.js +384 -449
- data/dist/controllers/accordion_controller.js +174 -55
- data/dist/controllers/alert_dialog_controller.js +903 -235
- data/dist/controllers/announcer_controller.js +238 -205
- data/dist/controllers/aspect_ratio_controller.js +0 -10
- data/dist/controllers/auto_submit_controller.js +414 -145
- data/dist/controllers/avatar_controller.js +185 -107
- data/dist/controllers/breadcrumb_controller.js +0 -130
- data/dist/controllers/bulk_select_controller.js +282 -58
- data/dist/controllers/calendar_controller.js +228 -260
- data/dist/controllers/carousel_controller.js +536 -436
- data/dist/controllers/character_counter_controller.js +493 -207
- data/dist/controllers/checkbox_controller.js +38 -74
- data/dist/controllers/clipboard_controller.js +261 -149
- data/dist/controllers/collapsible_controller.js +304 -120
- data/dist/controllers/color_picker_controller.js +178 -146
- data/dist/controllers/combobox_controller.js +522 -156
- data/dist/controllers/command_palette_controller.js +1156 -395
- data/dist/controllers/conditional_fields_controller.js +300 -171
- data/dist/controllers/confirm_controller.js +980 -262
- data/dist/controllers/context_menu_controller.js +346 -153
- data/dist/controllers/count_up_controller.js +73 -21
- data/dist/controllers/countdown_controller.js +281 -158
- data/dist/controllers/currency_input_controller.js +575 -225
- data/dist/controllers/data_grid_controller.js +248 -214
- data/dist/controllers/date_range_picker_controller.js +338 -235
- data/dist/controllers/dialog_controller.js +899 -237
- data/dist/controllers/direct_upload_controller.js +97 -167
- data/dist/controllers/dirty_form_controller.js +72 -69
- data/dist/controllers/dismissible_controller.js +196 -24
- data/dist/controllers/drawer_controller.js +974 -355
- data/dist/controllers/dropdown_controller.js +339 -96
- data/dist/controllers/editable_controller.js +315 -88
- data/dist/controllers/empty_state_controller.js +79 -58
- data/dist/controllers/file_dropzone_controller.js +322 -211
- data/dist/controllers/filter_controller.js +77 -42
- data/dist/controllers/flash_controller.js +238 -347
- data/dist/controllers/focus_controller.js +679 -240
- data/dist/controllers/form_field_controller.js +156 -132
- data/dist/controllers/form_validation_controller.js +31 -97
- data/dist/controllers/frame_loading_controller.js +275 -217
- data/dist/controllers/highlight_controller.js +107 -75
- data/dist/controllers/hover_card_controller.js +327 -130
- data/dist/controllers/idle_controller.js +271 -101
- data/dist/controllers/input_mask_controller.js +483 -97
- data/dist/controllers/intersection_controller.js +150 -106
- data/dist/controllers/lazy_frame_controller.js +24 -68
- data/dist/controllers/listbox_controller.js +523 -240
- data/dist/controllers/local_time_controller.js +102 -77
- data/dist/controllers/masonry_controller.js +80 -96
- data/dist/controllers/menu_controller.js +302 -188
- data/dist/controllers/menubar_controller.js +232 -345
- data/dist/controllers/meter_controller.js +417 -87
- data/dist/controllers/multi_select_controller.js +785 -393
- data/dist/controllers/navigation_menu_controller.js +190 -250
- data/dist/controllers/nested_form_controller.js +396 -192
- data/dist/controllers/network_status_controller.js +268 -62
- data/dist/controllers/number_input_controller.js +610 -303
- data/dist/controllers/optimistic_controller.js +45 -87
- data/dist/controllers/otp_controller.js +658 -255
- data/dist/controllers/overflow_indicator_controller.js +122 -113
- data/dist/controllers/overflow_menu_controller.js +470 -295
- data/dist/controllers/pagination_controller.js +332 -123
- data/dist/controllers/password_reveal_controller.js +392 -121
- data/dist/controllers/password_strength_controller.js +111 -158
- data/dist/controllers/persist_controller.js +165 -111
- data/dist/controllers/pointer_drag_controller.js +434 -188
- data/dist/controllers/popover_controller.js +269 -113
- data/dist/controllers/portal_controller.js +215 -76
- data/dist/controllers/preview_guard_controller.js +243 -116
- data/dist/controllers/progress_controller.js +391 -78
- data/dist/controllers/radio_group_controller.js +278 -211
- data/dist/controllers/range_slider_controller.js +220 -113
- data/dist/controllers/rating_controller.js +458 -211
- data/dist/controllers/read_more_controller.js +138 -63
- data/dist/controllers/reading_progress_controller.js +166 -137
- data/dist/controllers/relative_time_controller.js +211 -112
- data/dist/controllers/{reset_before_cache_controller.js → reset_on_restore_controller.js} +103 -38
- data/dist/controllers/resizable_controller.js +299 -150
- data/dist/controllers/roving_controller.js +49 -88
- data/dist/controllers/scroll_area_controller.js +490 -246
- data/dist/controllers/scroll_restore_controller.js +14 -55
- data/dist/controllers/scroll_visibility_controller.js +432 -149
- data/dist/controllers/scrollspy_controller.js +193 -248
- data/dist/controllers/separator_controller.js +285 -142
- data/dist/controllers/sidebar_controller.js +1069 -404
- data/dist/controllers/skeleton_controller.js +259 -101
- data/dist/controllers/slider_controller.js +187 -86
- data/dist/controllers/smart_sticky_header_controller.js +173 -66
- data/dist/controllers/sortable_controller.js +26 -125
- data/dist/controllers/spinner_controller.js +288 -173
- data/dist/controllers/step_indicator_controller.js +105 -46
- data/dist/controllers/stepper_controller.js +120 -73
- data/dist/controllers/stick_to_bottom_controller.js +275 -140
- data/dist/controllers/sticky_observer_controller.js +220 -49
- data/dist/controllers/submit_once_controller.js +376 -227
- data/dist/controllers/switch_controller.js +201 -84
- data/dist/controllers/tabs_controller.js +41 -30
- data/dist/controllers/tags_input_controller.js +293 -173
- data/dist/controllers/textarea_autosize_controller.js +108 -74
- data/dist/controllers/theme_controller.js +140 -116
- data/dist/controllers/time_picker_controller.js +331 -127
- data/dist/controllers/toast_controller.js +244 -324
- data/dist/controllers/toggle_group_controller.js +252 -191
- data/dist/controllers/toolbar_controller.js +219 -120
- data/dist/controllers/tooltip_controller.js +158 -124
- data/dist/controllers/transition_controller.js +123 -119
- data/dist/controllers/tree_view_controller.js +283 -369
- data/dist/index.js +8573 -10410
- data/dist/positioning/index.js +154 -47
- data/lib/generators/stimeo/install/install_generator.rb +7 -2
- data/lib/generators/stimeo/install/templates/stimeo.js +19 -6
- data/lib/stimeo/ui/version.rb +1 -1
- metadata +3 -3
|
@@ -1,8 +1,158 @@
|
|
|
1
1
|
import { Controller } from '@hotwired/stimulus';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
function setAttributeIfChanged(element, name, value, beforeWrite) {
|
|
4
|
+
if (element.getAttribute(name) === value) return false;
|
|
5
|
+
if (value === null) element.removeAttribute(name);
|
|
6
|
+
else element.setAttribute(name, value);
|
|
7
|
+
return true;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function sharedRegistry(key, create) {
|
|
11
|
+
const symbol = Symbol.for(key);
|
|
12
|
+
const scope = globalThis;
|
|
13
|
+
const existing = scope[symbol];
|
|
14
|
+
if (existing !== void 0) return existing;
|
|
15
|
+
const registry2 = create();
|
|
16
|
+
Object.defineProperty(globalThis, symbol, { value: registry2 });
|
|
17
|
+
return registry2;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
var registry = sharedRegistry(
|
|
21
|
+
"stimeo-ui.attribute-lease.registry.v1",
|
|
22
|
+
() => ({ stacks: /* @__PURE__ */ new WeakMap(), names: /* @__PURE__ */ new Map() })
|
|
23
|
+
);
|
|
24
|
+
function isAttributeValue(value) {
|
|
25
|
+
return value === null || typeof value === "string";
|
|
26
|
+
}
|
|
27
|
+
function readLeaseRecord(element, record) {
|
|
28
|
+
try {
|
|
29
|
+
const parsed = JSON.parse(element.getAttribute(record) ?? "null");
|
|
30
|
+
if (!Array.isArray(parsed)) return void 0;
|
|
31
|
+
const [original, written, ...rest] = parsed;
|
|
32
|
+
const linked = rest[0] !== false;
|
|
33
|
+
const beneath = linked ? rest : rest.slice(1);
|
|
34
|
+
if (!beneath.every((under) => typeof under === "string")) return void 0;
|
|
35
|
+
return isAttributeValue(original) && isAttributeValue(written) ? { original, written, linked, beneath } : void 0;
|
|
36
|
+
} catch {
|
|
37
|
+
return void 0;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function attributeStack(element, attribute) {
|
|
41
|
+
const byAttribute = registry.stacks.get(element) ?? /* @__PURE__ */ new Map();
|
|
42
|
+
registry.stacks.set(element, byAttribute);
|
|
43
|
+
const known = byAttribute.get(attribute);
|
|
44
|
+
if (known) return known;
|
|
45
|
+
const found = [];
|
|
46
|
+
for (const record of registry.names.get(attribute) ?? []) {
|
|
47
|
+
const read = readLeaseRecord(element, record);
|
|
48
|
+
if (read) found.push({ ...read, record });
|
|
49
|
+
}
|
|
50
|
+
const depth = ({ beneath }) => beneath.filter((name) => found.some((other) => other.record === name)).length;
|
|
51
|
+
const stack = found.sort((one, other) => depth(one) - depth(other)).map(({ record, written, original, linked }, index) => ({
|
|
52
|
+
lease: null,
|
|
53
|
+
record,
|
|
54
|
+
written,
|
|
55
|
+
original,
|
|
56
|
+
linked: linked && index > 0
|
|
57
|
+
}));
|
|
58
|
+
byAttribute.set(attribute, stack);
|
|
59
|
+
return stack;
|
|
60
|
+
}
|
|
61
|
+
function findHold(stack, lease, record) {
|
|
62
|
+
return stack.find((held) => held.lease === lease) ?? stack.find((held) => held.lease === null && held.record === record);
|
|
63
|
+
}
|
|
64
|
+
var AttributeLease = class {
|
|
65
|
+
#attribute;
|
|
66
|
+
#record;
|
|
67
|
+
#base;
|
|
68
|
+
#preserveAuthor;
|
|
69
|
+
#held = /* @__PURE__ */ new Set();
|
|
70
|
+
constructor(attribute, owner, options = {}) {
|
|
71
|
+
this.#attribute = attribute;
|
|
72
|
+
this.#record = `data-${owner}-${attribute}-lease`;
|
|
73
|
+
this.#base = options.base === true;
|
|
74
|
+
this.#preserveAuthor = options.preserveAuthor === true;
|
|
75
|
+
const names = registry.names;
|
|
76
|
+
names.set(attribute, (names.get(attribute) ?? /* @__PURE__ */ new Set()).add(this.#record));
|
|
77
|
+
}
|
|
78
|
+
write(element, value) {
|
|
79
|
+
const stack = attributeStack(element, this.#attribute);
|
|
80
|
+
const hold = this.#take(stack) ?? this.#acquire(element, stack);
|
|
81
|
+
this.#held.add(element);
|
|
82
|
+
const shows = !this.#covered(element, stack, hold);
|
|
83
|
+
hold.written = value;
|
|
84
|
+
this.#mark(element, stack);
|
|
85
|
+
if (shows) this.#reflect(element, value);
|
|
86
|
+
}
|
|
87
|
+
return(element) {
|
|
88
|
+
const held = this.#held.delete(element);
|
|
89
|
+
const known = registry.stacks.get(element)?.has(this.#attribute);
|
|
90
|
+
const stack = held || known || element.hasAttribute(this.#record) ? attributeStack(element, this.#attribute) : [];
|
|
91
|
+
const hold = this.#take(stack);
|
|
92
|
+
if (!hold) return false;
|
|
93
|
+
const carries = element.getAttribute(this.#attribute) === hold.written;
|
|
94
|
+
const owns = carries && !this.#covered(element, stack, hold);
|
|
95
|
+
const index = stack.indexOf(hold);
|
|
96
|
+
stack.splice(index, 1);
|
|
97
|
+
const above = stack[index];
|
|
98
|
+
if (above) above.linked &&= hold.linked;
|
|
99
|
+
this.#mark(element, stack);
|
|
100
|
+
if (!owns) return true;
|
|
101
|
+
const below = hold.linked ? stack[index - 1] : void 0;
|
|
102
|
+
this.#reflect(element, below ? below.written : hold.original);
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
returnAll() {
|
|
106
|
+
for (const element of Array.from(this.#held)) this.return(element);
|
|
107
|
+
}
|
|
108
|
+
#take(stack) {
|
|
109
|
+
const hold = findHold(stack, this, this.#record);
|
|
110
|
+
if (hold) hold.lease = this;
|
|
111
|
+
return hold;
|
|
112
|
+
}
|
|
113
|
+
#acquire(element, stack) {
|
|
114
|
+
const current = element.getAttribute(this.#attribute);
|
|
115
|
+
const top = stack.at(-1);
|
|
116
|
+
const bottom = stack[0];
|
|
117
|
+
const over = top?.written === current ? top : void 0;
|
|
118
|
+
const from = this.#base ? this.#preserveAuthor && !stack.some((held) => held.written === current) ? void 0 : bottom : over;
|
|
119
|
+
const hold = {
|
|
120
|
+
lease: this,
|
|
121
|
+
record: this.#record,
|
|
122
|
+
written: current,
|
|
123
|
+
original: from ? from.original : current,
|
|
124
|
+
linked: !this.#base && over !== void 0
|
|
125
|
+
};
|
|
126
|
+
if (this.#base && bottom) {
|
|
127
|
+
bottom.linked = true;
|
|
128
|
+
stack.unshift(hold);
|
|
129
|
+
} else stack.push(hold);
|
|
130
|
+
return hold;
|
|
131
|
+
}
|
|
132
|
+
#covered(element, stack, hold) {
|
|
133
|
+
const current = element.getAttribute(this.#attribute);
|
|
134
|
+
return stack.slice(stack.indexOf(hold) + 1).some((above) => above.written === current);
|
|
135
|
+
}
|
|
136
|
+
#mark(element, stack) {
|
|
137
|
+
const records = /* @__PURE__ */ new Map([[this.#record, null]]);
|
|
138
|
+
stack.forEach((hold, index) => {
|
|
139
|
+
const names = stack.slice(0, index).map((under) => under.record);
|
|
140
|
+
const beneath = [...new Set(names.reverse())].filter((name) => name !== hold.record);
|
|
141
|
+
const marker = hold.linked || beneath.length === 0 ? [] : [false];
|
|
142
|
+
records.set(
|
|
143
|
+
hold.record,
|
|
144
|
+
hold.written === hold.original && beneath.length === 0 && index === stack.length - 1 ? null : JSON.stringify([hold.original, hold.written, ...marker, ...beneath])
|
|
145
|
+
);
|
|
146
|
+
});
|
|
147
|
+
for (const [name, recorded] of records) {
|
|
148
|
+
setAttributeIfChanged(element, name, recorded);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
#reflect(element, value) {
|
|
152
|
+
setAttributeIfChanged(element, this.#attribute, value);
|
|
153
|
+
}
|
|
154
|
+
};
|
|
4
155
|
|
|
5
|
-
// src/utils/declared_value.ts
|
|
6
156
|
function parseDeclared(raw, parse, fallback) {
|
|
7
157
|
try {
|
|
8
158
|
return parse(raw);
|
|
@@ -11,7 +161,6 @@ function parseDeclared(raw, parse, fallback) {
|
|
|
11
161
|
}
|
|
12
162
|
}
|
|
13
163
|
|
|
14
|
-
// src/utils/intersection_watcher.ts
|
|
15
164
|
function isBeforeRootStart(entry) {
|
|
16
165
|
const rect = entry.boundingClientRect;
|
|
17
166
|
if (rect.width === 0 && rect.height === 0) return false;
|
|
@@ -30,30 +179,12 @@ var IntersectionWatcher = class {
|
|
|
30
179
|
constructor(onEntries) {
|
|
31
180
|
this.#onEntries = onEntries;
|
|
32
181
|
}
|
|
33
|
-
/** Whether an observer is live (started, `IntersectionObserver` supported). */
|
|
34
182
|
get active() {
|
|
35
183
|
return this.#active;
|
|
36
184
|
}
|
|
37
|
-
/** Whether the live observer discarded configured options after construction failed. */
|
|
38
185
|
get usingPlatformDefaults() {
|
|
39
186
|
return this.#usingPlatformDefaults;
|
|
40
187
|
}
|
|
41
|
-
/**
|
|
42
|
-
* (Re)creates the observer and observes `targets`. Returns `false` — leaving
|
|
43
|
-
* the watcher inert — without `IntersectionObserver` support (very old
|
|
44
|
-
* browsers; the caller's no-JS fallback stays in charge) or with no targets.
|
|
45
|
-
* If initial construction with the configured options fails, the watcher
|
|
46
|
-
* warns and retries once with the same root and platform defaults. A
|
|
47
|
-
* `rootSelector` that does not parse resolves to the viewport (see
|
|
48
|
-
* {@link IntersectionWatchOptions.rootSelector}), so a typo never fails the
|
|
49
|
-
* call.
|
|
50
|
-
*
|
|
51
|
-
* @throws The fallback constructor error if both construction attempts fail,
|
|
52
|
-
* or whatever the platform throws from `observe()`. The exception is passed
|
|
53
|
-
* through unchanged, but the watcher rolls back first: every target observed
|
|
54
|
-
* so far is released and `active` stays `false`, so a caller that retries
|
|
55
|
-
* starts from a clean slate.
|
|
56
|
-
*/
|
|
57
188
|
start(targets, options = {}) {
|
|
58
189
|
this.stop();
|
|
59
190
|
if (typeof IntersectionObserver === "undefined") return false;
|
|
@@ -91,14 +222,6 @@ var IntersectionWatcher = class {
|
|
|
91
222
|
throw error;
|
|
92
223
|
}
|
|
93
224
|
}
|
|
94
|
-
/**
|
|
95
|
-
* Re-delivers `target`'s CURRENT intersection state: `IntersectionObserver`
|
|
96
|
-
* only reports *changes*, but `observe()` always reports the present state,
|
|
97
|
-
* so unobserve→observe turns "still intersecting" into a fresh callback.
|
|
98
|
-
*
|
|
99
|
-
* @throws Whatever `unobserve()`/`observe()` throws. The watcher is stopped
|
|
100
|
-
* first, so it never stays live with a half-rearmed target.
|
|
101
|
-
*/
|
|
102
225
|
rearm(target) {
|
|
103
226
|
if (!this.#observer) return;
|
|
104
227
|
try {
|
|
@@ -109,7 +232,6 @@ var IntersectionWatcher = class {
|
|
|
109
232
|
throw error;
|
|
110
233
|
}
|
|
111
234
|
}
|
|
112
|
-
/** Severs the observer; late queued callbacks become no-ops via the guard. */
|
|
113
235
|
stop() {
|
|
114
236
|
this.#active = false;
|
|
115
237
|
this.#observer?.disconnect();
|
|
@@ -118,19 +240,64 @@ var IntersectionWatcher = class {
|
|
|
118
240
|
}
|
|
119
241
|
};
|
|
120
242
|
|
|
121
|
-
|
|
122
|
-
|
|
243
|
+
var NUMBER_BOUNDS = {
|
|
244
|
+
finite: { finite: true }};
|
|
245
|
+
function matchesNumberBounds(value, bounds) {
|
|
246
|
+
if (!Number.isFinite(value)) {
|
|
247
|
+
const direction = value === Infinity ? "positive" : value === -Infinity ? "negative" : null;
|
|
248
|
+
if (direction === null) return false;
|
|
249
|
+
if (bounds.allowInfinity !== "both" && bounds.allowInfinity !== direction) return false;
|
|
250
|
+
}
|
|
251
|
+
if (bounds.min !== void 0 && value < bounds.min) return false;
|
|
252
|
+
if (bounds.max !== void 0 && value > bounds.max) return false;
|
|
253
|
+
if (bounds.exclusiveMin !== void 0 && value <= bounds.exclusiveMin) return false;
|
|
254
|
+
if (bounds.integer && !Number.isInteger(value)) return false;
|
|
255
|
+
if (bounds.allowedValues !== void 0 && !bounds.allowedValues.includes(value)) return false;
|
|
256
|
+
return true;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function readNumber(raw, fallback, bounds) {
|
|
260
|
+
return matchesNumberBounds(raw, bounds) ? raw : fallback;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
var NumberValueReader = class {
|
|
264
|
+
#lastRejected = /* @__PURE__ */ new Map();
|
|
265
|
+
read(owner, name, raw, fallback, bounds) {
|
|
266
|
+
const resolved = readNumber(raw, fallback, bounds);
|
|
267
|
+
if (matchesNumberBounds(raw, bounds)) {
|
|
268
|
+
this.#lastRejected.delete(name);
|
|
269
|
+
return resolved;
|
|
270
|
+
}
|
|
271
|
+
const attribute = `data-${owner.identifier}-${name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}-value`;
|
|
272
|
+
const literal = owner.element.getAttribute(attribute);
|
|
273
|
+
if (literal === null) {
|
|
274
|
+
this.#lastRejected.delete(name);
|
|
275
|
+
return resolved;
|
|
276
|
+
}
|
|
277
|
+
if (this.#lastRejected.get(name) !== literal) {
|
|
278
|
+
this.#lastRejected.set(name, literal);
|
|
279
|
+
console.warn(
|
|
280
|
+
`Stimeo UI: "${owner.identifier}" has an invalid number Value "${name}" declaration ${JSON.stringify(literal)}; using ${fallback}.`
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
return resolved;
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
var StickyObserverController = class _StickyObserverController extends Controller {
|
|
288
|
+
#numbers = new NumberValueReader();
|
|
123
289
|
static targets = ["sentinel", "element"];
|
|
124
290
|
static values = {
|
|
125
291
|
rootSelector: { type: String, default: "" },
|
|
126
292
|
offset: { type: Number, default: 0 }
|
|
127
293
|
};
|
|
294
|
+
static valueConstraints = {
|
|
295
|
+
offset: NUMBER_BOUNDS.finite
|
|
296
|
+
};
|
|
128
297
|
static events = ["change"];
|
|
129
|
-
/** Shared IO plumbing (support guard, root resolution, active guard). */
|
|
130
298
|
#watcher = new IntersectionWatcher((entries) => this.#onIntersect(entries));
|
|
131
|
-
|
|
299
|
+
#stuckLease = new AttributeLease("data-stuck", this.identifier);
|
|
132
300
|
#stuck = null;
|
|
133
|
-
/** Target currently owned by the watcher; comparing it avoids duplicate restarts. */
|
|
134
301
|
#observedSentinel = null;
|
|
135
302
|
#connected = false;
|
|
136
303
|
#onIntersect(entries) {
|
|
@@ -150,25 +317,24 @@ var StickyObserverController = class extends Controller {
|
|
|
150
317
|
this.#observedSentinel = null;
|
|
151
318
|
this.#stuck = null;
|
|
152
319
|
}
|
|
153
|
-
/** Starts observation when a sentinel is inserted after connection. */
|
|
154
320
|
sentinelTargetConnected() {
|
|
155
321
|
if (this.#connected) this.#syncObserver();
|
|
156
322
|
}
|
|
157
|
-
/** Stops or transfers observation when the current sentinel is removed. */
|
|
158
323
|
sentinelTargetDisconnected() {
|
|
159
324
|
if (this.#connected) this.#syncObserver();
|
|
160
325
|
}
|
|
161
|
-
/** Reflects the last snapshot onto an element inserted after that snapshot. */
|
|
162
326
|
elementTargetConnected(element) {
|
|
163
|
-
if (this.#stuck !== null)
|
|
164
|
-
|
|
327
|
+
if (this.#stuck !== null) this.#stuckLease.write(element, String(this.#stuck));
|
|
328
|
+
}
|
|
329
|
+
elementTargetDisconnected(element) {
|
|
330
|
+
if (!this.elementTargets.includes(element)) this.#stuckLease.return(element);
|
|
331
|
+
if (this.#connected && this.#stuck !== null && this.hasElementTarget) {
|
|
332
|
+
this.#stuckLease.write(this.elementTarget, String(this.#stuck));
|
|
165
333
|
}
|
|
166
334
|
}
|
|
167
|
-
/** Rebuilds the observer when Turbo morphs the configured root. */
|
|
168
335
|
rootSelectorValueChanged() {
|
|
169
336
|
if (this.#connected) this.#syncObserver(true);
|
|
170
337
|
}
|
|
171
|
-
/** Rebuilds the observer when Turbo morphs the configured top offset. */
|
|
172
338
|
offsetValueChanged() {
|
|
173
339
|
if (this.#connected) this.#syncObserver(true);
|
|
174
340
|
}
|
|
@@ -178,8 +344,8 @@ var StickyObserverController = class extends Controller {
|
|
|
178
344
|
this.#watcher.stop();
|
|
179
345
|
this.#observedSentinel = null;
|
|
180
346
|
if (!sentinel) return;
|
|
181
|
-
const configuredOffset = this
|
|
182
|
-
const offset =
|
|
347
|
+
const configuredOffset = this.#safeOffset;
|
|
348
|
+
const offset = configuredOffset;
|
|
183
349
|
const started = this.#watcher.start(sentinel, {
|
|
184
350
|
rootSelector: this.rootSelectorValue,
|
|
185
351
|
rootMargin: `${-offset}px 0px 0px 0px`,
|
|
@@ -187,17 +353,22 @@ var StickyObserverController = class extends Controller {
|
|
|
187
353
|
});
|
|
188
354
|
if (started) this.#observedSentinel = sentinel;
|
|
189
355
|
}
|
|
190
|
-
/** Reflects the stuck state onto the sticky element and emits `change`. */
|
|
191
356
|
#setStuck(next) {
|
|
192
357
|
if (next === this.#stuck) return;
|
|
193
358
|
this.#stuck = next;
|
|
194
|
-
if (this.hasElementTarget)
|
|
195
|
-
this.elementTarget.setAttribute("data-stuck", next ? "true" : "false");
|
|
196
|
-
}
|
|
359
|
+
if (this.hasElementTarget) this.#stuckLease.write(this.elementTarget, String(next));
|
|
197
360
|
this.dispatch("change", { detail: { stuck: next } });
|
|
198
361
|
}
|
|
362
|
+
get #safeOffset() {
|
|
363
|
+
return this.#numbers.read(
|
|
364
|
+
this,
|
|
365
|
+
"offset",
|
|
366
|
+
this.offsetValue,
|
|
367
|
+
_StickyObserverController.values.offset.default,
|
|
368
|
+
_StickyObserverController.valueConstraints.offset
|
|
369
|
+
);
|
|
370
|
+
}
|
|
199
371
|
};
|
|
200
372
|
|
|
201
373
|
export { StickyObserverController };
|
|
202
|
-
//# sourceMappingURL=sticky_observer_controller.js.map
|
|
203
374
|
//# sourceMappingURL=sticky_observer_controller.js.map
|