@supersoniks/concorde 4.9.5 → 4.9.7
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 +158 -158
- package/concorde-core.es.js +2746 -2621
- package/dist/concorde-core.bundle.js +158 -158
- package/dist/concorde-core.es.js +2746 -2621
- package/dist/js/core/components/functional/queue/queue.js +210 -18
- package/dist/js/core/components/functional/queue/queue.js.map +1 -1
- package/dist/js/core/utils/HTML.js +40 -15
- package/dist/js/core/utils/HTML.js.map +1 -1
- package/dist/types/core/components/functional/queue/queue.d.ts +33 -3
- package/dist/types/core/components/functional/queue/queue.d.ts.map +1 -1
- package/dist/types/core/utils/HTML.d.ts +15 -0
- package/dist/types/core/utils/HTML.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/core/components/functional/queue/queue.md +0 -0
- package/src/core/components/functional/queue/queue.ts +227 -23
- package/src/core/components/ui/button/button.md +0 -0
- package/src/core/components/ui/button/button.ts +0 -0
- package/src/core/utils/HTML.ts +52 -17
- package/src/docs/code.ts +0 -0
- package/src/docs/search/docs-search.json +123 -28
- package/src/docs/search/markdown-renderer.ts +0 -0
|
@@ -74,6 +74,7 @@ export default class Queue extends Subscriber(LitElement, {} as QueueProps) {
|
|
|
74
74
|
this.requestId = 0;
|
|
75
75
|
this.isFirstRequest = true;
|
|
76
76
|
this.nextHadEvent = false;
|
|
77
|
+
this.teardownScrollGuard();
|
|
77
78
|
this.publisher.set({});
|
|
78
79
|
super.disconnectedCallback();
|
|
79
80
|
return;
|
|
@@ -105,7 +106,8 @@ export default class Queue extends Subscriber(LitElement, {} as QueueProps) {
|
|
|
105
106
|
HTML.getAncestorAttributeValue(this.parentElement, "dataProvider") ||
|
|
106
107
|
"";
|
|
107
108
|
}
|
|
108
|
-
this.
|
|
109
|
+
this.captureScrollPosition();
|
|
110
|
+
this.ensureScrollGuard();
|
|
109
111
|
super.connectedCallback();
|
|
110
112
|
this.publisher.set({});
|
|
111
113
|
this.key = this.getAttribute("key");
|
|
@@ -161,6 +163,9 @@ export default class Queue extends Subscriber(LitElement, {} as QueueProps) {
|
|
|
161
163
|
const searchHash = searchParams.toString();
|
|
162
164
|
if (searchHash == this.searchHash && !this.isFirstRequest) return;
|
|
163
165
|
this.searchHash = searchHash;
|
|
166
|
+
// Nouvelle recherche : ne pas restaurer l'ancienne position de scroll
|
|
167
|
+
this.storedScrollX = 0;
|
|
168
|
+
this.storedScrollY = 0;
|
|
164
169
|
|
|
165
170
|
/**
|
|
166
171
|
* on reset les données avant de lancer la requète
|
|
@@ -187,35 +192,233 @@ export default class Queue extends Subscriber(LitElement, {} as QueueProps) {
|
|
|
187
192
|
this.isFirstRequest = false;
|
|
188
193
|
};
|
|
189
194
|
|
|
190
|
-
|
|
195
|
+
/**
|
|
196
|
+
* Conteneur scrollé (élément ou window) — Safari / iOS only.
|
|
197
|
+
* Stock = dernière position user (touch/wheel/pointer + settle inertie).
|
|
198
|
+
* Restore = updated (enfants Lit) + MutationObserver (subscribers / modal).
|
|
199
|
+
*/
|
|
200
|
+
scrollRoot: HTMLElement | Window | null = null;
|
|
201
|
+
storedScrollY = 0;
|
|
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>;
|
|
208
|
+
private mutationObserver: MutationObserver | null = null;
|
|
209
|
+
private boundScrollTarget: EventTarget | null = null;
|
|
210
|
+
private restoreGeneration = 0;
|
|
211
|
+
|
|
212
|
+
resolveScrollRoot(): HTMLElement | Window {
|
|
213
|
+
const ancestor = HTML.getScrollingAncestor(this);
|
|
214
|
+
if (
|
|
215
|
+
!ancestor ||
|
|
216
|
+
ancestor === document.documentElement ||
|
|
217
|
+
ancestor === document.body
|
|
218
|
+
) {
|
|
219
|
+
return window;
|
|
220
|
+
}
|
|
221
|
+
return ancestor as HTMLElement;
|
|
222
|
+
}
|
|
223
|
+
|
|
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() {
|
|
191
242
|
if (!this.isSafari()) {
|
|
192
243
|
return;
|
|
193
244
|
}
|
|
194
|
-
this.
|
|
195
|
-
|
|
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
|
+
}
|
|
196
272
|
}
|
|
197
273
|
|
|
198
274
|
isSafari() {
|
|
199
275
|
return /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
|
|
200
276
|
}
|
|
201
277
|
|
|
278
|
+
private markUserScrollEngaged = () => {
|
|
279
|
+
this.userScrollEngaged = true;
|
|
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) {
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
const root = this.resolveScrollRoot();
|
|
320
|
+
const target: EventTarget = root === window ? window : root;
|
|
321
|
+
if (this.scrollGuardActive && this.boundScrollTarget === target) {
|
|
322
|
+
this.scrollRoot = root;
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
this.teardownScrollGuard();
|
|
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
|
+
|
|
354
|
+
this.mutationObserver = new MutationObserver(() => {
|
|
355
|
+
this.scheduleRestoreAfterChildren();
|
|
356
|
+
});
|
|
357
|
+
this.mutationObserver.observe(this, {
|
|
358
|
+
childList: true,
|
|
359
|
+
subtree: true,
|
|
360
|
+
characterData: true,
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
teardownScrollGuard() {
|
|
365
|
+
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
|
+
this.mutationObserver?.disconnect();
|
|
383
|
+
this.mutationObserver = null;
|
|
384
|
+
this.scrollGuardActive = false;
|
|
385
|
+
this.scrollRoot = null;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Attend la fin des updates Lit de la queue + sonic-list, puis 2 rAF (layout Safari).
|
|
390
|
+
* Ne pas await updateComplete *dans* updated (deadlock) — on sort du cycle via void async.
|
|
391
|
+
*/
|
|
392
|
+
scheduleRestoreAfterChildren() {
|
|
393
|
+
if (!this.isSafari() || !this.isConnected) {
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
const generation = ++this.restoreGeneration;
|
|
397
|
+
void (async () => {
|
|
398
|
+
await this.updateComplete;
|
|
399
|
+
if (generation !== this.restoreGeneration || !this.isConnected) return;
|
|
400
|
+
|
|
401
|
+
const lists = Array.from(
|
|
402
|
+
this.querySelectorAll<LitElement>("sonic-list")
|
|
403
|
+
);
|
|
404
|
+
if (lists.length) {
|
|
405
|
+
await Promise.all(lists.map((list) => list.updateComplete));
|
|
406
|
+
}
|
|
407
|
+
if (generation !== this.restoreGeneration || !this.isConnected) return;
|
|
408
|
+
|
|
409
|
+
await new Promise<void>((r) => requestAnimationFrame(() => r()));
|
|
410
|
+
await new Promise<void>((r) => requestAnimationFrame(() => r()));
|
|
411
|
+
if (generation !== this.restoreGeneration || !this.isConnected) return;
|
|
412
|
+
|
|
413
|
+
this.restoreScrollPosition();
|
|
414
|
+
})();
|
|
415
|
+
}
|
|
416
|
+
|
|
202
417
|
protected updated(_changedProperties: PropertyValues): void {
|
|
203
418
|
if (this.isSafari()) {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
) {
|
|
208
|
-
window.scrollTo(this.storedScrollX, this.storedScrollY);
|
|
209
|
-
}
|
|
210
|
-
//Hack pour éviter que le scroll ne retourne en haut si safari
|
|
211
|
-
window.requestAnimationFrame(() => {
|
|
212
|
-
if (
|
|
213
|
-
Math.abs(this.storedScrollX - window.scrollX) > 10 ||
|
|
214
|
-
Math.abs(this.storedScrollY - window.scrollY) > 10
|
|
215
|
-
) {
|
|
216
|
-
window.scrollTo(this.storedScrollX, this.storedScrollY);
|
|
217
|
-
}
|
|
218
|
-
});
|
|
419
|
+
this.ensureScrollGuard();
|
|
420
|
+
this.restoreScrollPosition();
|
|
421
|
+
this.scheduleRestoreAfterChildren();
|
|
219
422
|
}
|
|
220
423
|
|
|
221
424
|
super.updated(_changedProperties);
|
|
@@ -326,11 +529,12 @@ export default class Queue extends Subscriber(LitElement, {} as QueueProps) {
|
|
|
326
529
|
this.lastRequestTime = new Date().getTime();
|
|
327
530
|
}
|
|
328
531
|
|
|
329
|
-
storedScrollY = 0;
|
|
330
|
-
storedScrollX = 0;
|
|
331
|
-
|
|
332
532
|
render() {
|
|
333
|
-
|
|
533
|
+
// Ne pas écraser le stock avec un jump Safari : uniquement sync du root / guard.
|
|
534
|
+
if (this.isSafari()) {
|
|
535
|
+
this.scrollRoot = this.resolveScrollRoot();
|
|
536
|
+
this.ensureScrollGuard();
|
|
537
|
+
}
|
|
334
538
|
if (!Array.isArray(this.props)) {
|
|
335
539
|
return nothing;
|
|
336
540
|
}
|
|
File without changes
|
|
File without changes
|
package/src/core/utils/HTML.ts
CHANGED
|
@@ -24,6 +24,20 @@ class HTML {
|
|
|
24
24
|
}, {});
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Ancêtre suivant dans l'arbre composé : slot de projection, sinon parentNode / shadow host.
|
|
29
|
+
* Nécessaire pour trouver un scroller/clipper dans le shadow d'un ancêtre (ex. #modal.custom-scroll).
|
|
30
|
+
*/
|
|
31
|
+
static nextComposedAncestor(
|
|
32
|
+
node: SearchableDomElement,
|
|
33
|
+
): SearchableDomElement | null {
|
|
34
|
+
if (node instanceof Element && node.assignedSlot) {
|
|
35
|
+
return node.assignedSlot;
|
|
36
|
+
}
|
|
37
|
+
const parent = node.parentNode || (node as ShadowRoot).host;
|
|
38
|
+
return (parent as SearchableDomElement) || null;
|
|
39
|
+
}
|
|
40
|
+
|
|
27
41
|
static everyAncestors(
|
|
28
42
|
node: SearchableDomElement,
|
|
29
43
|
callback: (node: SearchableDomElement) => /*continue ?*/ boolean
|
|
@@ -36,29 +50,50 @@ class HTML {
|
|
|
36
50
|
}
|
|
37
51
|
}
|
|
38
52
|
|
|
39
|
-
static
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
53
|
+
private static overflowMatches(
|
|
54
|
+
style: CSSStyleDeclaration,
|
|
55
|
+
values: readonly string[],
|
|
56
|
+
): boolean {
|
|
57
|
+
return values.includes(style.overflowY) || values.includes(style.overflowX);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private static findAncestorByOverflow(
|
|
61
|
+
node: SearchableDomElement,
|
|
62
|
+
overflowValues: readonly string[],
|
|
63
|
+
): SearchableDomElement | null {
|
|
64
|
+
let current: SearchableDomElement | null = node;
|
|
65
|
+
while (current) {
|
|
66
|
+
if (current instanceof Element && current.nodeType === 1) {
|
|
67
|
+
const style = window.getComputedStyle(current);
|
|
68
|
+
if (HTML.overflowMatches(style, overflowValues)) {
|
|
69
|
+
return current;
|
|
54
70
|
}
|
|
55
71
|
}
|
|
56
|
-
|
|
57
|
-
(node as ShadowRoot).host) as SearchableDomElement;
|
|
72
|
+
current = HTML.nextComposedAncestor(current);
|
|
58
73
|
}
|
|
59
74
|
return null;
|
|
60
75
|
}
|
|
61
76
|
|
|
77
|
+
/**
|
|
78
|
+
* Ancêtre qui clippe (inclut overflow:hidden) — ex. clamp de sonic-pop.
|
|
79
|
+
* Reconnaît aussi overflow:overlay (.custom-scroll) et traverse les slots.
|
|
80
|
+
*/
|
|
81
|
+
static getScrollableAncestor(node: SearchableDomElement) {
|
|
82
|
+
return HTML.findAncestorByOverflow(node, [
|
|
83
|
+
"auto",
|
|
84
|
+
"scroll",
|
|
85
|
+
"hidden",
|
|
86
|
+
"overlay",
|
|
87
|
+
]);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Ancêtre réellement scrollable (pas overflow:hidden) — ex. restore scroll Safari de sonic-queue.
|
|
92
|
+
*/
|
|
93
|
+
static getScrollingAncestor(node: SearchableDomElement) {
|
|
94
|
+
return HTML.findAncestorByOverflow(node, ["auto", "scroll", "overlay"]);
|
|
95
|
+
}
|
|
96
|
+
|
|
62
97
|
/** Noms d'attribut DOM à tester (Lit reflect → minuscules, ex. serviceURL → serviceurl). */
|
|
63
98
|
static scopeAttributeNames(attributeName: string): string[] {
|
|
64
99
|
const lower = attributeName.toLowerCase();
|
package/src/docs/code.ts
CHANGED
|
File without changes
|
|
@@ -350,14 +350,19 @@
|
|
|
350
350
|
"core/components/functional/queue/queue.md": {
|
|
351
351
|
"title": "Queue",
|
|
352
352
|
"hashes": {
|
|
353
|
-
"lazy-load
|
|
353
|
+
"lazy-load": {
|
|
354
354
|
"count": 1,
|
|
355
|
-
"title": "Lazy load
|
|
355
|
+
"title": "Lazy load",
|
|
356
356
|
"type": "paragraph"
|
|
357
357
|
},
|
|
358
|
-
"
|
|
358
|
+
"manual-next-signal--supplement": {
|
|
359
359
|
"count": 1,
|
|
360
|
-
"title": "
|
|
360
|
+
"title": "Manual next (signal) — supplement",
|
|
361
|
+
"type": "paragraph"
|
|
362
|
+
},
|
|
363
|
+
"simple-batch-no-pagination": {
|
|
364
|
+
"count": 1,
|
|
365
|
+
"title": "Simple batch (no pagination)",
|
|
361
366
|
"type": "paragraph"
|
|
362
367
|
}
|
|
363
368
|
}
|
|
@@ -820,14 +825,14 @@
|
|
|
820
825
|
}
|
|
821
826
|
},
|
|
822
827
|
{
|
|
823
|
-
"search": "
|
|
828
|
+
"search": "In Lit apps, use defineQueue + .config on <sonic-queue> — typed Endpoint, DataProviderKey, enums. See src/docs/example/docs-queue-demos.ts.\n",
|
|
824
829
|
"files": {
|
|
825
830
|
"core/components/functional/queue/queue.md": {
|
|
826
831
|
"title": "Queue",
|
|
827
832
|
"hashes": {
|
|
828
|
-
"
|
|
833
|
+
"queue": {
|
|
829
834
|
"count": 1,
|
|
830
|
-
"title": "
|
|
835
|
+
"title": "",
|
|
831
836
|
"type": "paragraph"
|
|
832
837
|
}
|
|
833
838
|
}
|
|
@@ -835,14 +840,29 @@
|
|
|
835
840
|
}
|
|
836
841
|
},
|
|
837
842
|
{
|
|
838
|
-
"search": "
|
|
843
|
+
"search": "Required: endpoint + items. Add rowsAt when rows live in a nested property. Everything else has sensible defaults (strategy: Lazy, pageSize: 10, offset/limit query names).\n",
|
|
839
844
|
"files": {
|
|
840
845
|
"core/components/functional/queue/queue.md": {
|
|
841
846
|
"title": "Queue",
|
|
842
847
|
"hashes": {
|
|
843
|
-
"
|
|
848
|
+
"queue": {
|
|
844
849
|
"count": 1,
|
|
845
|
-
"title": "
|
|
850
|
+
"title": "",
|
|
851
|
+
"type": "paragraph"
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
},
|
|
857
|
+
{
|
|
858
|
+
"search": "Layout (class, id, …) stays on <sonic-queue> — not inside .config.\n",
|
|
859
|
+
"files": {
|
|
860
|
+
"core/components/functional/queue/queue.md": {
|
|
861
|
+
"title": "Queue",
|
|
862
|
+
"hashes": {
|
|
863
|
+
"queue": {
|
|
864
|
+
"count": 1,
|
|
865
|
+
"title": "",
|
|
846
866
|
"type": "paragraph"
|
|
847
867
|
}
|
|
848
868
|
}
|
|
@@ -850,14 +870,14 @@
|
|
|
850
870
|
}
|
|
851
871
|
},
|
|
852
872
|
{
|
|
853
|
-
"search": "
|
|
873
|
+
"search": "When pagination is enabled (default QueueLoadStrategy.Lazy), the queue:\n",
|
|
854
874
|
"files": {
|
|
855
875
|
"core/components/functional/queue/queue.md": {
|
|
856
876
|
"title": "Queue",
|
|
857
877
|
"hashes": {
|
|
858
|
-
"lazy-load
|
|
878
|
+
"lazy-load": {
|
|
859
879
|
"count": 1,
|
|
860
|
-
"title": "Lazy load
|
|
880
|
+
"title": "Lazy load",
|
|
861
881
|
"type": "paragraph"
|
|
862
882
|
}
|
|
863
883
|
}
|
|
@@ -865,14 +885,14 @@
|
|
|
865
885
|
}
|
|
866
886
|
},
|
|
867
887
|
{
|
|
868
|
-
"search": "
|
|
888
|
+
"search": "Mock: GET /docs-mock-api/api/users?offset=…&limit=… (paginateUsers in src/docs/mock-api/router.ts).\n",
|
|
869
889
|
"files": {
|
|
870
890
|
"core/components/functional/queue/queue.md": {
|
|
871
891
|
"title": "Queue",
|
|
872
892
|
"hashes": {
|
|
873
|
-
"lazy-load
|
|
893
|
+
"lazy-load": {
|
|
874
894
|
"count": 1,
|
|
875
|
-
"title": "Lazy load
|
|
895
|
+
"title": "Lazy load",
|
|
876
896
|
"type": "paragraph"
|
|
877
897
|
}
|
|
878
898
|
}
|
|
@@ -880,14 +900,14 @@
|
|
|
880
900
|
}
|
|
881
901
|
},
|
|
882
902
|
{
|
|
883
|
-
"search": "
|
|
903
|
+
"search": "Wrap the queue in a fixed height with overflow-y-auto so lazy load triggers when scrolling inside the box.\n",
|
|
884
904
|
"files": {
|
|
885
905
|
"core/components/functional/queue/queue.md": {
|
|
886
906
|
"title": "Queue",
|
|
887
907
|
"hashes": {
|
|
888
|
-
"
|
|
908
|
+
"lazy-load": {
|
|
889
909
|
"count": 1,
|
|
890
|
-
"title": "
|
|
910
|
+
"title": "Lazy load",
|
|
891
911
|
"type": "paragraph"
|
|
892
912
|
}
|
|
893
913
|
}
|
|
@@ -895,7 +915,22 @@
|
|
|
895
915
|
}
|
|
896
916
|
},
|
|
897
917
|
{
|
|
898
|
-
"search": "
|
|
918
|
+
"search": "Without pagination offset ( $offset not generated), the queue behaves as a single batch — see geo communes below.\n",
|
|
919
|
+
"files": {
|
|
920
|
+
"core/components/functional/queue/queue.md": {
|
|
921
|
+
"title": "Queue",
|
|
922
|
+
"hashes": {
|
|
923
|
+
"lazy-load": {
|
|
924
|
+
"count": 1,
|
|
925
|
+
"title": "Lazy load",
|
|
926
|
+
"type": "paragraph"
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
},
|
|
932
|
+
{
|
|
933
|
+
"search": "Pass a filter DataProviderKey in defineQueue (same publisher as formDataProvider on the search form). On change, the queue:\n",
|
|
899
934
|
"files": {
|
|
900
935
|
"core/components/functional/queue/queue.md": {
|
|
901
936
|
"title": "Queue",
|
|
@@ -910,7 +945,7 @@
|
|
|
910
945
|
}
|
|
911
946
|
},
|
|
912
947
|
{
|
|
913
|
-
"search": "Use filteredFields only when the form has
|
|
948
|
+
"search": "Use filteredFields in the config only when the form has fields that must not be sent to the API.\n",
|
|
914
949
|
"files": {
|
|
915
950
|
"core/components/functional/queue/queue.md": {
|
|
916
951
|
"title": "Queue",
|
|
@@ -925,7 +960,7 @@
|
|
|
925
960
|
}
|
|
926
961
|
},
|
|
927
962
|
{
|
|
928
|
-
"search": "The
|
|
963
|
+
"search": "The users demo above wires docsQueueUsersFilterKey + sonic-input name="q". Mock filter: filterDocsUsers in src/docs/mock-api/fixtures.ts.\n",
|
|
929
964
|
"files": {
|
|
930
965
|
"core/components/functional/queue/queue.md": {
|
|
931
966
|
"title": "Queue",
|
|
@@ -940,14 +975,14 @@
|
|
|
940
975
|
}
|
|
941
976
|
},
|
|
942
977
|
{
|
|
943
|
-
"search": "
|
|
978
|
+
"search": "Default lazy scroll loads the next batch automatically. For a button that triggers the next batch via publishers — without touching the queue instance — use QueueLoadStrategy.Signal and a void next key (invalidate(), same idea as triggerKey on @get).\n",
|
|
944
979
|
"files": {
|
|
945
980
|
"core/components/functional/queue/queue.md": {
|
|
946
981
|
"title": "Queue",
|
|
947
982
|
"hashes": {
|
|
948
|
-
"
|
|
983
|
+
"manual-next-signal--supplement": {
|
|
949
984
|
"count": 1,
|
|
950
|
-
"title": "
|
|
985
|
+
"title": "Manual next (signal) — supplement",
|
|
951
986
|
"type": "paragraph"
|
|
952
987
|
}
|
|
953
988
|
}
|
|
@@ -955,14 +990,44 @@
|
|
|
955
990
|
}
|
|
956
991
|
},
|
|
957
992
|
{
|
|
958
|
-
"search": "
|
|
993
|
+
"search": "state exposes QueuePublisherState on the queue dataProvider: meta (e.g. total), loadedCount, hasMore. Wire sonic-button invalidate to the same next key.\n",
|
|
959
994
|
"files": {
|
|
960
995
|
"core/components/functional/queue/queue.md": {
|
|
961
996
|
"title": "Queue",
|
|
962
997
|
"hashes": {
|
|
963
|
-
"
|
|
998
|
+
"manual-next-signal--supplement": {
|
|
964
999
|
"count": 1,
|
|
965
|
-
"title": "
|
|
1000
|
+
"title": "Manual next (signal) — supplement",
|
|
1001
|
+
"type": "paragraph"
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
},
|
|
1007
|
+
{
|
|
1008
|
+
"search": "Elsewhere: dp(usersNextKey).invalidate().\n",
|
|
1009
|
+
"files": {
|
|
1010
|
+
"core/components/functional/queue/queue.md": {
|
|
1011
|
+
"title": "Queue",
|
|
1012
|
+
"hashes": {
|
|
1013
|
+
"manual-next-signal--supplement": {
|
|
1014
|
+
"count": 1,
|
|
1015
|
+
"title": "Manual next (signal) — supplement",
|
|
1016
|
+
"type": "paragraph"
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
},
|
|
1022
|
+
{
|
|
1023
|
+
"search": "Geo communes: one request, one batch ($limit only).\n",
|
|
1024
|
+
"files": {
|
|
1025
|
+
"core/components/functional/queue/queue.md": {
|
|
1026
|
+
"title": "Queue",
|
|
1027
|
+
"hashes": {
|
|
1028
|
+
"simple-batch-no-pagination": {
|
|
1029
|
+
"count": 1,
|
|
1030
|
+
"title": "Simple batch (no pagination)",
|
|
966
1031
|
"type": "paragraph"
|
|
967
1032
|
}
|
|
968
1033
|
}
|
|
@@ -2269,6 +2334,21 @@
|
|
|
2269
2334
|
}
|
|
2270
2335
|
}
|
|
2271
2336
|
},
|
|
2337
|
+
{
|
|
2338
|
+
"search": "Emit a publisher signal on click (same idea as triggerKey on @get, or dataNextProvider on sonic-queue). Value is the publisher path.\n",
|
|
2339
|
+
"files": {
|
|
2340
|
+
"core/components/ui/button/button.md": {
|
|
2341
|
+
"title": "Button",
|
|
2342
|
+
"hashes": {
|
|
2343
|
+
"invalidate": {
|
|
2344
|
+
"count": 1,
|
|
2345
|
+
"title": "Invalidate",
|
|
2346
|
+
"type": "paragraph"
|
|
2347
|
+
}
|
|
2348
|
+
}
|
|
2349
|
+
}
|
|
2350
|
+
}
|
|
2351
|
+
},
|
|
2272
2352
|
{
|
|
2273
2353
|
"search": "Captcha",
|
|
2274
2354
|
"files": {
|
|
@@ -6934,6 +7014,21 @@
|
|
|
6934
7014
|
}
|
|
6935
7015
|
}
|
|
6936
7016
|
},
|
|
7017
|
+
{
|
|
7018
|
+
"search": "Everything after <!-- concorde-ai --> is replaced on each ai-init run (idempotent). Do not paste a full copy of the Concorde guide above the marker — put project-specific notes in AGENTS.concorde.md (overlay). If the preamble duplicates package AGENTS.md, ai-init slims or drops it and prints a warning. Legacy filenames AGENTS.overlay.md / AGENTS.starter.md are still accepted with a deprecation warning.\n",
|
|
7019
|
+
"files": {
|
|
7020
|
+
"docs/_getting-started/ai-agents.md": {
|
|
7021
|
+
"title": "AI agents (skills & AGENTS.md)",
|
|
7022
|
+
"hashes": {
|
|
7023
|
+
"agentsmd-policy-non-invasive": {
|
|
7024
|
+
"count": 1,
|
|
7025
|
+
"title": "`AGENTS.md` policy (non-invasive)",
|
|
7026
|
+
"type": "paragraph"
|
|
7027
|
+
}
|
|
7028
|
+
}
|
|
7029
|
+
}
|
|
7030
|
+
}
|
|
7031
|
+
},
|
|
6937
7032
|
{
|
|
6938
7033
|
"search": "Source files in the npm package: node_modules/@supersoniks/concorde/ai/ (see also ai/README.md in the Concorde repository).\n",
|
|
6939
7034
|
"files": {
|
|
File without changes
|