@supersoniks/concorde 4.9.7 → 4.9.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build-infos.json +1 -1
- package/concorde-core.bundle.js +180 -180
- package/concorde-core.es.js +3660 -3508
- package/dist/concorde-core.bundle.js +180 -180
- package/dist/concorde-core.es.js +3660 -3508
- package/dist/js/core/components/functional/queue/queue.js +25 -153
- package/dist/js/core/components/functional/queue/queue.js.map +1 -1
- package/dist/js/core/components/ui/_css/scroll.js +2 -0
- package/dist/js/core/components/ui/_css/scroll.js.map +1 -1
- package/dist/js/core/utils/SafariScrollGuard.js +384 -0
- package/dist/js/core/utils/SafariScrollGuard.js.map +1 -0
- package/dist/js/utils.js +3 -0
- package/dist/js/utils.js.map +1 -1
- package/dist/types/core/components/functional/queue/queue.d.ts +8 -24
- package/dist/types/core/components/functional/queue/queue.d.ts.map +1 -1
- package/dist/types/core/components/ui/_css/scroll.d.ts.map +1 -1
- package/dist/types/core/utils/SafariScrollGuard.d.ts +36 -0
- package/dist/types/core/utils/SafariScrollGuard.d.ts.map +1 -0
- package/dist/types/utils.d.ts +1 -0
- package/dist/types/utils.d.ts.map +1 -1
- package/package.json +6 -2
- package/src/core/components/functional/queue/queue.ts +33 -164
- package/src/core/components/ui/_css/scroll.ts +3 -0
- package/src/core/utils/SafariScrollGuard.ts +429 -0
- package/src/docs/search/docs-search.json +28 -108
- package/src/docs/tailwind/css/tailwind.css +0 -0
- package/src/tsconfig.json +3 -0
- package/src/tsconfig.tsbuildinfo +1 -1
- package/src/utils.ts +13 -0
- package/vite.config.mts +4 -0
- package/vitest.setup.ts +22 -0
|
@@ -5,6 +5,13 @@ import { map } from "lit/directives/map.js";
|
|
|
5
5
|
import DataProvider, { PublisherManager } from "@supersoniks/concorde/core/utils/PublisherProxy";
|
|
6
6
|
import "@supersoniks/concorde/core/components/functional/list/list";
|
|
7
7
|
import { HTML } from "@supersoniks/concorde/utils";
|
|
8
|
+
import {
|
|
9
|
+
isSafariScrollEngine,
|
|
10
|
+
resetScrollRoot,
|
|
11
|
+
restoreScrollRoot,
|
|
12
|
+
watchScrollRoot,
|
|
13
|
+
type ScrollRoot,
|
|
14
|
+
} from "@supersoniks/concorde/core/utils/SafariScrollGuard";
|
|
8
15
|
import { ListItems } from "@supersoniks/concorde/core/components/functional/list/list";
|
|
9
16
|
|
|
10
17
|
type QueueItem = {
|
|
@@ -74,7 +81,7 @@ export default class Queue extends Subscriber(LitElement, {} as QueueProps) {
|
|
|
74
81
|
this.requestId = 0;
|
|
75
82
|
this.isFirstRequest = true;
|
|
76
83
|
this.nextHadEvent = false;
|
|
77
|
-
this.
|
|
84
|
+
this.teardownScrollSync();
|
|
78
85
|
this.publisher.set({});
|
|
79
86
|
super.disconnectedCallback();
|
|
80
87
|
return;
|
|
@@ -106,8 +113,7 @@ export default class Queue extends Subscriber(LitElement, {} as QueueProps) {
|
|
|
106
113
|
HTML.getAncestorAttributeValue(this.parentElement, "dataProvider") ||
|
|
107
114
|
"";
|
|
108
115
|
}
|
|
109
|
-
this.
|
|
110
|
-
this.ensureScrollGuard();
|
|
116
|
+
this.ensureScrollSync();
|
|
111
117
|
super.connectedCallback();
|
|
112
118
|
this.publisher.set({});
|
|
113
119
|
this.key = this.getAttribute("key");
|
|
@@ -164,8 +170,7 @@ export default class Queue extends Subscriber(LitElement, {} as QueueProps) {
|
|
|
164
170
|
if (searchHash == this.searchHash && !this.isFirstRequest) return;
|
|
165
171
|
this.searchHash = searchHash;
|
|
166
172
|
// Nouvelle recherche : ne pas restaurer l'ancienne position de scroll
|
|
167
|
-
this.
|
|
168
|
-
this.storedScrollY = 0;
|
|
173
|
+
resetScrollRoot(this.resolveScrollRoot(), { x: 0, y: 0 });
|
|
169
174
|
|
|
170
175
|
/**
|
|
171
176
|
* on reset les données avant de lancer la requète
|
|
@@ -193,23 +198,15 @@ export default class Queue extends Subscriber(LitElement, {} as QueueProps) {
|
|
|
193
198
|
};
|
|
194
199
|
|
|
195
200
|
/**
|
|
196
|
-
* Conteneur scrollé
|
|
197
|
-
*
|
|
198
|
-
* Restore = updated (enfants Lit) + MutationObserver (subscribers / modal).
|
|
201
|
+
* Conteneur scrollé — Safari : stock/restore via SafariScrollGuard
|
|
202
|
+
* (capture intent user globale). Ici : sync root + restore après Lit.
|
|
199
203
|
*/
|
|
200
|
-
scrollRoot:
|
|
201
|
-
|
|
202
|
-
storedScrollX = 0;
|
|
203
|
-
private static readonly SCROLL_SETTLE_MS = 150;
|
|
204
|
-
private userScrollEngaged = false;
|
|
205
|
-
private restoringScroll = false;
|
|
206
|
-
private scrollGuardActive = false;
|
|
207
|
-
private scrollSettleTimer?: ReturnType<typeof setTimeout>;
|
|
204
|
+
scrollRoot: ScrollRoot | null = null;
|
|
205
|
+
private scrollSyncActive = false;
|
|
208
206
|
private mutationObserver: MutationObserver | null = null;
|
|
209
|
-
private boundScrollTarget: EventTarget | null = null;
|
|
210
207
|
private restoreGeneration = 0;
|
|
211
208
|
|
|
212
|
-
resolveScrollRoot():
|
|
209
|
+
resolveScrollRoot(): ScrollRoot {
|
|
213
210
|
const ancestor = HTML.getScrollingAncestor(this);
|
|
214
211
|
if (
|
|
215
212
|
!ancestor ||
|
|
@@ -221,136 +218,21 @@ export default class Queue extends Subscriber(LitElement, {} as QueueProps) {
|
|
|
221
218
|
return ancestor as HTMLElement;
|
|
222
219
|
}
|
|
223
220
|
|
|
224
|
-
/** Lit la position courante — ne pas appeler sur un scroll “fantôme” Safari. */
|
|
225
|
-
captureScrollPosition() {
|
|
226
|
-
if (!this.isSafari()) {
|
|
227
|
-
return;
|
|
228
|
-
}
|
|
229
|
-
const root = this.resolveScrollRoot();
|
|
230
|
-
this.scrollRoot = root;
|
|
231
|
-
if (root === window) {
|
|
232
|
-
this.storedScrollX = window.scrollX;
|
|
233
|
-
this.storedScrollY = window.scrollY;
|
|
234
|
-
} else {
|
|
235
|
-
const el = root as HTMLElement;
|
|
236
|
-
this.storedScrollX = el.scrollLeft;
|
|
237
|
-
this.storedScrollY = el.scrollTop;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
restoreScrollPosition() {
|
|
242
|
-
if (!this.isSafari()) {
|
|
243
|
-
return;
|
|
244
|
-
}
|
|
245
|
-
const root = this.scrollRoot ?? this.resolveScrollRoot();
|
|
246
|
-
const x = this.storedScrollX;
|
|
247
|
-
const y = this.storedScrollY;
|
|
248
|
-
this.restoringScroll = true;
|
|
249
|
-
try {
|
|
250
|
-
if (root === window) {
|
|
251
|
-
if (
|
|
252
|
-
Math.abs(x - window.scrollX) > 10 ||
|
|
253
|
-
Math.abs(y - window.scrollY) > 10
|
|
254
|
-
) {
|
|
255
|
-
window.scrollTo(x, y);
|
|
256
|
-
}
|
|
257
|
-
} else {
|
|
258
|
-
const el = root as HTMLElement;
|
|
259
|
-
if (
|
|
260
|
-
Math.abs(x - el.scrollLeft) > 10 ||
|
|
261
|
-
Math.abs(y - el.scrollTop) > 10
|
|
262
|
-
) {
|
|
263
|
-
el.scrollLeft = x;
|
|
264
|
-
el.scrollTop = y;
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
} finally {
|
|
268
|
-
requestAnimationFrame(() => {
|
|
269
|
-
this.restoringScroll = false;
|
|
270
|
-
});
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
|
|
274
221
|
isSafari() {
|
|
275
|
-
return
|
|
222
|
+
return isSafariScrollEngine();
|
|
276
223
|
}
|
|
277
224
|
|
|
278
|
-
|
|
279
|
-
this.
|
|
280
|
-
};
|
|
281
|
-
|
|
282
|
-
private armScrollSettle = () => {
|
|
283
|
-
clearTimeout(this.scrollSettleTimer);
|
|
284
|
-
this.scrollSettleTimer = setTimeout(() => {
|
|
285
|
-
this.userScrollEngaged = false;
|
|
286
|
-
this.scrollSettleTimer = undefined;
|
|
287
|
-
}, Queue.SCROLL_SETTLE_MS);
|
|
288
|
-
};
|
|
289
|
-
|
|
290
|
-
private onScrollRootScroll = () => {
|
|
291
|
-
if (!this.isSafari() || this.restoringScroll || !this.userScrollEngaged) {
|
|
292
|
-
return;
|
|
293
|
-
}
|
|
294
|
-
this.captureScrollPosition();
|
|
295
|
-
// iOS : l’inertie continue après touchend → prolonger l’engagement
|
|
296
|
-
this.armScrollSettle();
|
|
297
|
-
};
|
|
298
|
-
|
|
299
|
-
private onUserScrollIntent = () => {
|
|
300
|
-
if (!this.isSafari()) return;
|
|
301
|
-
this.markUserScrollEngaged();
|
|
302
|
-
};
|
|
303
|
-
|
|
304
|
-
private onUserScrollIntentEnd = () => {
|
|
305
|
-
if (!this.isSafari()) return;
|
|
306
|
-
this.armScrollSettle();
|
|
307
|
-
};
|
|
308
|
-
|
|
309
|
-
private onWheelIntent = () => {
|
|
310
|
-
if (!this.isSafari()) return;
|
|
311
|
-
this.markUserScrollEngaged();
|
|
312
|
-
this.armScrollSettle();
|
|
313
|
-
};
|
|
314
|
-
|
|
315
|
-
ensureScrollGuard() {
|
|
316
|
-
if (!this.isSafari() || !this.isConnected) {
|
|
225
|
+
ensureScrollSync() {
|
|
226
|
+
if (!isSafariScrollEngine() || !this.isConnected) {
|
|
317
227
|
return;
|
|
318
228
|
}
|
|
319
229
|
const root = this.resolveScrollRoot();
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
230
|
+
this.scrollRoot = root;
|
|
231
|
+
watchScrollRoot(root);
|
|
232
|
+
if (this.scrollSyncActive) {
|
|
323
233
|
return;
|
|
324
234
|
}
|
|
325
|
-
this.
|
|
326
|
-
this.scrollRoot = root;
|
|
327
|
-
this.boundScrollTarget = target;
|
|
328
|
-
this.scrollGuardActive = true;
|
|
329
|
-
|
|
330
|
-
target.addEventListener("scroll", this.onScrollRootScroll, {
|
|
331
|
-
passive: true,
|
|
332
|
-
});
|
|
333
|
-
// Intent user : touch (iOS), wheel, pointer (scrollbar desktop Safari)
|
|
334
|
-
target.addEventListener("touchstart", this.onUserScrollIntent, {
|
|
335
|
-
passive: true,
|
|
336
|
-
});
|
|
337
|
-
target.addEventListener("touchend", this.onUserScrollIntentEnd, {
|
|
338
|
-
passive: true,
|
|
339
|
-
});
|
|
340
|
-
target.addEventListener("touchcancel", this.onUserScrollIntentEnd, {
|
|
341
|
-
passive: true,
|
|
342
|
-
});
|
|
343
|
-
target.addEventListener("wheel", this.onWheelIntent, { passive: true });
|
|
344
|
-
target.addEventListener("pointerdown", this.onUserScrollIntent, {
|
|
345
|
-
passive: true,
|
|
346
|
-
});
|
|
347
|
-
target.addEventListener("pointerup", this.onUserScrollIntentEnd, {
|
|
348
|
-
passive: true,
|
|
349
|
-
});
|
|
350
|
-
target.addEventListener("pointercancel", this.onUserScrollIntentEnd, {
|
|
351
|
-
passive: true,
|
|
352
|
-
});
|
|
353
|
-
|
|
235
|
+
this.scrollSyncActive = true;
|
|
354
236
|
this.mutationObserver = new MutationObserver(() => {
|
|
355
237
|
this.scheduleRestoreAfterChildren();
|
|
356
238
|
});
|
|
@@ -361,27 +243,11 @@ export default class Queue extends Subscriber(LitElement, {} as QueueProps) {
|
|
|
361
243
|
});
|
|
362
244
|
}
|
|
363
245
|
|
|
364
|
-
|
|
246
|
+
teardownScrollSync() {
|
|
365
247
|
this.restoreGeneration++;
|
|
366
|
-
clearTimeout(this.scrollSettleTimer);
|
|
367
|
-
this.scrollSettleTimer = undefined;
|
|
368
|
-
this.userScrollEngaged = false;
|
|
369
|
-
this.restoringScroll = false;
|
|
370
|
-
if (this.boundScrollTarget) {
|
|
371
|
-
const target = this.boundScrollTarget;
|
|
372
|
-
target.removeEventListener("scroll", this.onScrollRootScroll);
|
|
373
|
-
target.removeEventListener("touchstart", this.onUserScrollIntent);
|
|
374
|
-
target.removeEventListener("touchend", this.onUserScrollIntentEnd);
|
|
375
|
-
target.removeEventListener("touchcancel", this.onUserScrollIntentEnd);
|
|
376
|
-
target.removeEventListener("wheel", this.onWheelIntent);
|
|
377
|
-
target.removeEventListener("pointerdown", this.onUserScrollIntent);
|
|
378
|
-
target.removeEventListener("pointerup", this.onUserScrollIntentEnd);
|
|
379
|
-
target.removeEventListener("pointercancel", this.onUserScrollIntentEnd);
|
|
380
|
-
this.boundScrollTarget = null;
|
|
381
|
-
}
|
|
382
248
|
this.mutationObserver?.disconnect();
|
|
383
249
|
this.mutationObserver = null;
|
|
384
|
-
this.
|
|
250
|
+
this.scrollSyncActive = false;
|
|
385
251
|
this.scrollRoot = null;
|
|
386
252
|
}
|
|
387
253
|
|
|
@@ -390,7 +256,7 @@ export default class Queue extends Subscriber(LitElement, {} as QueueProps) {
|
|
|
390
256
|
* Ne pas await updateComplete *dans* updated (deadlock) — on sort du cycle via void async.
|
|
391
257
|
*/
|
|
392
258
|
scheduleRestoreAfterChildren() {
|
|
393
|
-
if (!
|
|
259
|
+
if (!isSafariScrollEngine() || !this.isConnected) {
|
|
394
260
|
return;
|
|
395
261
|
}
|
|
396
262
|
const generation = ++this.restoreGeneration;
|
|
@@ -410,14 +276,17 @@ export default class Queue extends Subscriber(LitElement, {} as QueueProps) {
|
|
|
410
276
|
await new Promise<void>((r) => requestAnimationFrame(() => r()));
|
|
411
277
|
if (generation !== this.restoreGeneration || !this.isConnected) return;
|
|
412
278
|
|
|
413
|
-
this.
|
|
279
|
+
const root = this.scrollRoot ?? this.resolveScrollRoot();
|
|
280
|
+
watchScrollRoot(root);
|
|
281
|
+
restoreScrollRoot(root);
|
|
414
282
|
})();
|
|
415
283
|
}
|
|
416
284
|
|
|
417
285
|
protected updated(_changedProperties: PropertyValues): void {
|
|
418
|
-
if (
|
|
419
|
-
this.
|
|
420
|
-
this.
|
|
286
|
+
if (isSafariScrollEngine()) {
|
|
287
|
+
this.ensureScrollSync();
|
|
288
|
+
const root = this.scrollRoot ?? this.resolveScrollRoot();
|
|
289
|
+
restoreScrollRoot(root);
|
|
421
290
|
this.scheduleRestoreAfterChildren();
|
|
422
291
|
}
|
|
423
292
|
|
|
@@ -533,7 +402,7 @@ export default class Queue extends Subscriber(LitElement, {} as QueueProps) {
|
|
|
533
402
|
// Ne pas écraser le stock avec un jump Safari : uniquement sync du root / guard.
|
|
534
403
|
if (this.isSafari()) {
|
|
535
404
|
this.scrollRoot = this.resolveScrollRoot();
|
|
536
|
-
this.
|
|
405
|
+
this.ensureScrollSync();
|
|
537
406
|
}
|
|
538
407
|
if (!Array.isArray(this.props)) {
|
|
539
408
|
return nothing;
|