@supersoniks/concorde 4.5.0 → 4.5.2
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 +228 -227
- package/concorde-core.es.js +1871 -1820
- package/dist/concorde-core.bundle.js +228 -227
- package/dist/concorde-core.es.js +1871 -1820
- package/package.json +5 -1
- package/src/core/components/ui/modal/modal-utils.ts +46 -0
- package/src/core/components/ui/modal/modal.ts +33 -8
- package/src/core/decorators/api.ts +3 -3
- package/src/core/decorators/subscriber/ancestorAttribute.ts +5 -4
- package/src/core/decorators/subscriber/bind.ts +9 -7
- package/src/core/decorators/subscriber/common.ts +22 -2
- package/src/core/decorators/subscriber/dynamicPropertyWatch.spec.ts +125 -0
- package/src/core/decorators/subscriber/dynamicPropertyWatch.ts +157 -72
- package/src/core/decorators/subscriber/onAssign.ts +2 -2
- package/src/core/decorators/subscriber/publish.ts +2 -2
- package/src/docs/example/decorators-demo-bind-demos.ts +6 -2
- package/src/docs/example/decorators-demo-geo.ts +10 -9
- package/src/docs/example/decorators-demo-subscribe-publish-get-demos.ts +1 -5
- package/src/docs/example/decorators-demo.ts +2 -2
- package/src/docs/search/docs-search.json +0 -45
- package/src/tsconfig.json +12 -0
- package/src/tsconfig.tsbuildinfo +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@supersoniks/concorde",
|
|
3
|
-
"version": "4.5.
|
|
3
|
+
"version": "4.5.2",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "",
|
|
@@ -220,6 +220,9 @@
|
|
|
220
220
|
"./modal-title": "./src/core/components/ui/modal/modal-title.ts",
|
|
221
221
|
"./ui/modal-title": "./src/core/components/ui/modal/modal-title.ts",
|
|
222
222
|
"./ui/modal/modal-title": "./src/core/components/ui/modal/modal-title.ts",
|
|
223
|
+
"./modal-utils": "./src/core/components/ui/modal/modal-utils.ts",
|
|
224
|
+
"./ui/modal-utils": "./src/core/components/ui/modal/modal-utils.ts",
|
|
225
|
+
"./ui/modal/modal-utils": "./src/core/components/ui/modal/modal-utils.ts",
|
|
223
226
|
"./modal": "./src/core/components/ui/modal/modal.ts",
|
|
224
227
|
"./ui/modal": "./src/core/components/ui/modal/modal.ts",
|
|
225
228
|
"./pop": "./src/core/components/ui/pop/pop.ts",
|
|
@@ -283,6 +286,7 @@
|
|
|
283
286
|
"./decorators/subscriber/bind": "./src/core/decorators/subscriber/bind.ts",
|
|
284
287
|
"./decorators/subscriber/common": "./src/core/decorators/subscriber/common.ts",
|
|
285
288
|
"./decorators/subscriber/dynamicPath": "./src/core/decorators/subscriber/dynamicPath.ts",
|
|
289
|
+
"./decorators/subscriber/dynamicPropertyWatch.spec": "./src/core/decorators/subscriber/dynamicPropertyWatch.spec.ts",
|
|
286
290
|
"./decorators/subscriber/dynamicPropertyWatch": "./src/core/decorators/subscriber/dynamicPropertyWatch.ts",
|
|
287
291
|
"./decorators/subscriber/onAssign": "./src/core/decorators/subscriber/onAssign.ts",
|
|
288
292
|
"./decorators/subscriber/publish.spec": "./src/core/decorators/subscriber/publish.spec.ts",
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { CSSResultGroup, CSSResult } from "lit";
|
|
2
|
+
|
|
3
|
+
export type ModalStyleSheetInput =
|
|
4
|
+
| CSSResultGroup
|
|
5
|
+
| CSSStyleSheet
|
|
6
|
+
| Array<CSSResultGroup | CSSStyleSheet>;
|
|
7
|
+
|
|
8
|
+
const styleSheetCache = new WeakMap<CSSResult, CSSStyleSheet>();
|
|
9
|
+
|
|
10
|
+
function cssResultToStyleSheet(styleResult: CSSResult): CSSStyleSheet {
|
|
11
|
+
const cachedStyleSheet = styleSheetCache.get(styleResult);
|
|
12
|
+
if (cachedStyleSheet) return cachedStyleSheet;
|
|
13
|
+
|
|
14
|
+
const styleSheet = new CSSStyleSheet();
|
|
15
|
+
styleSheet.replaceSync(styleResult.cssText);
|
|
16
|
+
styleSheetCache.set(styleResult, styleSheet);
|
|
17
|
+
return styleSheet;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function isCSSResult(value: unknown): value is CSSResult {
|
|
21
|
+
return value instanceof CSSResult;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function normalizeStyleSheets(
|
|
25
|
+
styleSheet?: ModalStyleSheetInput
|
|
26
|
+
): Array<CSSStyleSheet> {
|
|
27
|
+
if (!styleSheet) return [];
|
|
28
|
+
|
|
29
|
+
const styleSheets = Array.isArray(styleSheet) ? styleSheet : [styleSheet];
|
|
30
|
+
|
|
31
|
+
return styleSheets.flatMap((sheet) => {
|
|
32
|
+
if (Array.isArray(sheet)) {
|
|
33
|
+
return normalizeStyleSheets(sheet);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (sheet instanceof CSSStyleSheet) {
|
|
37
|
+
return [sheet];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (isCSSResult(sheet)) {
|
|
41
|
+
return [cssResultToStyleSheet(sheet)];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return [];
|
|
45
|
+
});
|
|
46
|
+
}
|
|
@@ -19,6 +19,7 @@ import LocationHandler from "@supersoniks/concorde/core/utils/LocationHandler";
|
|
|
19
19
|
import { unsafeHTML } from "lit/directives/unsafe-html.js";
|
|
20
20
|
import { ifDefined } from "lit/directives/if-defined.js";
|
|
21
21
|
import { ButtonType } from "../button/button";
|
|
22
|
+
import { ModalStyleSheetInput, normalizeStyleSheets } from "./modal-utils";
|
|
22
23
|
|
|
23
24
|
declare const window: ConcordeWindow;
|
|
24
25
|
declare type ModalCreateOptions = {
|
|
@@ -26,6 +27,7 @@ declare type ModalCreateOptions = {
|
|
|
26
27
|
subtitle?: string | DirectiveResult;
|
|
27
28
|
content?: string | DirectiveResult;
|
|
28
29
|
actions?: string | DirectiveResult;
|
|
30
|
+
styleSheet?: ModalStyleSheetInput;
|
|
29
31
|
zIndex?: string;
|
|
30
32
|
paddingX?: string;
|
|
31
33
|
paddingY?: string;
|
|
@@ -171,7 +173,8 @@ export class Modal extends Subscriber(LitElement) {
|
|
|
171
173
|
z-index: 20;
|
|
172
174
|
opacity: 0;
|
|
173
175
|
transform: scale(0);
|
|
174
|
-
transition:
|
|
176
|
+
transition:
|
|
177
|
+
transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1),
|
|
175
178
|
opacity 0.1s linear;
|
|
176
179
|
transform-origin: center center;
|
|
177
180
|
display: flex;
|
|
@@ -223,6 +226,7 @@ export class Modal extends Subscriber(LitElement) {
|
|
|
223
226
|
@property({ type: String }) width = "100%";
|
|
224
227
|
@property({ type: String }) height = "fit-content";
|
|
225
228
|
@property({ type: String }) effect: effectType = "slide";
|
|
229
|
+
@property({ attribute: false }) styleSheet?: ModalStyleSheetInput;
|
|
226
230
|
|
|
227
231
|
@property({ type: Object }) options?: ModalCreateOptions;
|
|
228
232
|
|
|
@@ -237,6 +241,7 @@ export class Modal extends Subscriber(LitElement) {
|
|
|
237
241
|
|
|
238
242
|
@state() private _animationState: "visible" | "in" | "out" | "hidden" =
|
|
239
243
|
"hidden";
|
|
244
|
+
private _adoptedStyleSheets: Array<CSSStyleSheet> = [];
|
|
240
245
|
|
|
241
246
|
static create(options: ModalCreateOptions): Modal {
|
|
242
247
|
const modal = document.createElement(tagName) as Modal;
|
|
@@ -260,6 +265,7 @@ export class Modal extends Subscriber(LitElement) {
|
|
|
260
265
|
if (options.noCloseButton) modal.noCloseButton = true;
|
|
261
266
|
if (options.closeButtonType)
|
|
262
267
|
modal.closeButtonType = options?.closeButtonType;
|
|
268
|
+
if (options.styleSheet) modal.styleSheet = options?.styleSheet;
|
|
263
269
|
if (options.paddingX) modal.paddingX = options?.paddingX;
|
|
264
270
|
if (options.paddingY) modal.paddingY = options?.paddingY;
|
|
265
271
|
if (options.zIndex) modal.zIndex = options?.zIndex;
|
|
@@ -327,6 +333,10 @@ export class Modal extends Subscriber(LitElement) {
|
|
|
327
333
|
} else if (changedToHidden && this._animationState === "visible") {
|
|
328
334
|
this.hide();
|
|
329
335
|
}
|
|
336
|
+
|
|
337
|
+
if (_changedProperties.has("styleSheet")) {
|
|
338
|
+
this.syncAdoptedStyleSheets();
|
|
339
|
+
}
|
|
330
340
|
}
|
|
331
341
|
|
|
332
342
|
// SI c'est en modal
|
|
@@ -393,6 +403,21 @@ export class Modal extends Subscriber(LitElement) {
|
|
|
393
403
|
`;
|
|
394
404
|
}
|
|
395
405
|
|
|
406
|
+
syncAdoptedStyleSheets() {
|
|
407
|
+
if (!(this.renderRoot instanceof ShadowRoot)) return;
|
|
408
|
+
|
|
409
|
+
const nextStyleSheets = normalizeStyleSheets(this.styleSheet);
|
|
410
|
+
const currentStyleSheets = this.renderRoot.adoptedStyleSheets.filter(
|
|
411
|
+
(sheet) => !this._adoptedStyleSheets.includes(sheet),
|
|
412
|
+
);
|
|
413
|
+
|
|
414
|
+
this.renderRoot.adoptedStyleSheets = [
|
|
415
|
+
...currentStyleSheets,
|
|
416
|
+
...nextStyleSheets,
|
|
417
|
+
];
|
|
418
|
+
this._adoptedStyleSheets = nextStyleSheets;
|
|
419
|
+
}
|
|
420
|
+
|
|
396
421
|
modalFragment(optionKey: keyof ModalCreateOptions) {
|
|
397
422
|
const optionValue: Object | PrimitiveType = this.options?.[optionKey];
|
|
398
423
|
if (!optionValue) return nothing;
|
|
@@ -440,7 +465,7 @@ export class Modal extends Subscriber(LitElement) {
|
|
|
440
465
|
this.visible = false;
|
|
441
466
|
if (this.hasAttribute("resetDataProviderOnHide")) {
|
|
442
467
|
PublisherManager.get(this.getAttribute("resetDataProviderOnHide")).set(
|
|
443
|
-
{}
|
|
468
|
+
{},
|
|
444
469
|
);
|
|
445
470
|
}
|
|
446
471
|
if (this.removeHashOnHide) {
|
|
@@ -470,7 +495,7 @@ export class Modal extends Subscriber(LitElement) {
|
|
|
470
495
|
if (e.key === "Escape") {
|
|
471
496
|
e.preventDefault();
|
|
472
497
|
const visibleModals = Modal.modals.filter(
|
|
473
|
-
(modal) => modal._animationState !== "hidden" && !modal.forceAction
|
|
498
|
+
(modal) => modal._animationState !== "hidden" && !modal.forceAction,
|
|
474
499
|
);
|
|
475
500
|
if (visibleModals.length > 0) {
|
|
476
501
|
const lastVisibleModal = visibleModals[visibleModals.length - 1];
|
|
@@ -529,8 +554,8 @@ export class Modal extends Subscriber(LitElement) {
|
|
|
529
554
|
easing: isEntering ? quartOut : quadOut,
|
|
530
555
|
fill: "both",
|
|
531
556
|
delay: delay,
|
|
532
|
-
}
|
|
533
|
-
)
|
|
557
|
+
},
|
|
558
|
+
),
|
|
534
559
|
);
|
|
535
560
|
animations.push(
|
|
536
561
|
element.animate(
|
|
@@ -540,13 +565,13 @@ export class Modal extends Subscriber(LitElement) {
|
|
|
540
565
|
easing: linear,
|
|
541
566
|
fill: "both",
|
|
542
567
|
delay: delay,
|
|
543
|
-
}
|
|
544
|
-
)
|
|
568
|
+
},
|
|
569
|
+
),
|
|
545
570
|
);
|
|
546
571
|
}
|
|
547
572
|
|
|
548
573
|
Promise.all(animations.map((anim) => anim.finished)).then(() =>
|
|
549
|
-
resolve()
|
|
574
|
+
resolve(),
|
|
550
575
|
);
|
|
551
576
|
});
|
|
552
577
|
}
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
} from "./subscriber/dynamicPath";
|
|
19
19
|
import {
|
|
20
20
|
getDynamicWatchKeys,
|
|
21
|
-
|
|
21
|
+
observeDynamicProperty,
|
|
22
22
|
} from "./subscriber/dynamicPropertyWatch";
|
|
23
23
|
import { getPublisherFromPath } from "./subscriber/publisherPath";
|
|
24
24
|
|
|
@@ -203,7 +203,7 @@ export function get<T, Ue = any, Uk = any>(
|
|
|
203
203
|
|
|
204
204
|
if (usesPublisherConfig) {
|
|
205
205
|
for (const dependency of mergedDynamicDependencies) {
|
|
206
|
-
const unsubscribe =
|
|
206
|
+
const unsubscribe = observeDynamicProperty(
|
|
207
207
|
getDynamicWatchKeys.watcherStore,
|
|
208
208
|
getDynamicWatchKeys.hooked,
|
|
209
209
|
component,
|
|
@@ -216,7 +216,7 @@ export function get<T, Ue = any, Uk = any>(
|
|
|
216
216
|
} else {
|
|
217
217
|
if (isDynamicPath) {
|
|
218
218
|
for (const dependency of endpointDynamicDependencies) {
|
|
219
|
-
const unsubscribe =
|
|
219
|
+
const unsubscribe = observeDynamicProperty(
|
|
220
220
|
getDynamicWatchKeys.watcherStore,
|
|
221
221
|
getDynamicWatchKeys.hooked,
|
|
222
222
|
component,
|
|
@@ -5,13 +5,14 @@ export function ancestorAttribute(attributeName: string) {
|
|
|
5
5
|
return function (target: unknown, propertyKey: string) {
|
|
6
6
|
if (!target) return;
|
|
7
7
|
setSubscribable(target);
|
|
8
|
-
|
|
9
|
-
(target as ConnectedComponent).__onConnected__((component) => {
|
|
8
|
+
(target as ConnectedComponent).__onBeforeConnected__((component) => {
|
|
10
9
|
const value = HTML.getAncestorAttributeValue(
|
|
11
10
|
component as any,
|
|
12
|
-
attributeName
|
|
11
|
+
attributeName,
|
|
13
12
|
);
|
|
14
|
-
|
|
13
|
+
if (value !== null) {
|
|
14
|
+
component[propertyKey] = value;
|
|
15
|
+
}
|
|
15
16
|
});
|
|
16
17
|
};
|
|
17
18
|
}
|
|
@@ -10,13 +10,13 @@ import {
|
|
|
10
10
|
} from "./dynamicPath";
|
|
11
11
|
import {
|
|
12
12
|
bindDynamicWatchKeys,
|
|
13
|
-
|
|
13
|
+
observeDynamicProperty,
|
|
14
14
|
} from "./dynamicPropertyWatch";
|
|
15
15
|
import { getPublisherFromPath } from "./publisherPath";
|
|
16
16
|
|
|
17
17
|
function bindImpl(
|
|
18
18
|
path: string,
|
|
19
|
-
options?: { reflect?: boolean }
|
|
19
|
+
options?: { reflect?: boolean },
|
|
20
20
|
): (target: unknown, propertyKey: string) => void {
|
|
21
21
|
const reflect = options?.reflect ?? false;
|
|
22
22
|
const dynamicDependencies = extractDynamicDependencies(path);
|
|
@@ -34,7 +34,7 @@ function bindImpl(
|
|
|
34
34
|
if (reflect) {
|
|
35
35
|
const existingDescriptor = Object.getOwnPropertyDescriptor(
|
|
36
36
|
target as any,
|
|
37
|
-
propertyKey
|
|
37
|
+
propertyKey,
|
|
38
38
|
);
|
|
39
39
|
const internalValueKey = `__bind_${propertyKey}_value__`;
|
|
40
40
|
const reflectUpdateFlagKey = `__bind_${propertyKey}_updating_from_publisher__`;
|
|
@@ -152,12 +152,12 @@ function bindImpl(
|
|
|
152
152
|
|
|
153
153
|
if (isDynamicPath) {
|
|
154
154
|
for (const dependency of dynamicDependencies) {
|
|
155
|
-
const unsubscribe =
|
|
155
|
+
const unsubscribe = observeDynamicProperty(
|
|
156
156
|
bindDynamicWatchKeys.watcherStore,
|
|
157
157
|
bindDynamicWatchKeys.hooked,
|
|
158
158
|
component,
|
|
159
159
|
dependency,
|
|
160
|
-
() => refreshSubscription()
|
|
160
|
+
() => refreshSubscription(),
|
|
161
161
|
);
|
|
162
162
|
state.cleanupWatchers.push(unsubscribe);
|
|
163
163
|
}
|
|
@@ -199,7 +199,10 @@ function bindImpl(
|
|
|
199
199
|
* @state()
|
|
200
200
|
* count: number = 0;
|
|
201
201
|
*/
|
|
202
|
-
export function bind(
|
|
202
|
+
export function bind(
|
|
203
|
+
path: string,
|
|
204
|
+
options?: { reflect?: boolean },
|
|
205
|
+
): (target: unknown, propertyKey: string) => void;
|
|
203
206
|
export function bind<T, U = any>(
|
|
204
207
|
key: DataProviderKey<T, U>,
|
|
205
208
|
options?: { reflect?: boolean },
|
|
@@ -214,4 +217,3 @@ export function bind(
|
|
|
214
217
|
const path = hasPath(pathOrKey) ? pathOrKey.path : pathOrKey;
|
|
215
218
|
return bindImpl(path, options);
|
|
216
219
|
}
|
|
217
|
-
|
|
@@ -1,12 +1,26 @@
|
|
|
1
1
|
type ConnectedCallback = (component: ConnectedComponent) => void;
|
|
2
2
|
|
|
3
3
|
export type ConnectedComponent = Record<string, unknown> & {
|
|
4
|
+
/** Hooks exécutés avant le connectedCallback d'origine (ex. lecture DOM ancêtre). */
|
|
5
|
+
__onBeforeConnected__: (callback: ConnectedCallback) => void;
|
|
6
|
+
/** Hooks exécutés après le connectedCallback d'origine (ex. abonnements). */
|
|
4
7
|
__onConnected__: (callback: ConnectedCallback) => void;
|
|
5
8
|
__onDisconnected__: (callback: ConnectedCallback) => void;
|
|
9
|
+
__beforeConnectedCallbackCalls__?: Set<ConnectedCallback>;
|
|
6
10
|
__connectedCallbackCalls__?: Set<ConnectedCallback>;
|
|
7
11
|
__disconnectedCallbackCalls__?: Set<ConnectedCallback>;
|
|
8
12
|
};
|
|
9
13
|
|
|
14
|
+
function onBeforeConnected(
|
|
15
|
+
this: ConnectedComponent,
|
|
16
|
+
callback: ConnectedCallback,
|
|
17
|
+
) {
|
|
18
|
+
if (!this.__beforeConnectedCallbackCalls__) {
|
|
19
|
+
this.__beforeConnectedCallbackCalls__ = new Set();
|
|
20
|
+
}
|
|
21
|
+
this.__beforeConnectedCallbackCalls__.add(callback);
|
|
22
|
+
}
|
|
23
|
+
|
|
10
24
|
function onConnected(this: ConnectedComponent, callback: ConnectedCallback) {
|
|
11
25
|
if (!this.__connectedCallbackCalls__) {
|
|
12
26
|
this.__connectedCallbackCalls__ = new Set();
|
|
@@ -25,15 +39,21 @@ export function setSubscribable(target: any) {
|
|
|
25
39
|
if (target.__is__setSubscribable__) return;
|
|
26
40
|
target.__is__setSubscribable__ = true;
|
|
27
41
|
|
|
42
|
+
target.__onBeforeConnected__ = onBeforeConnected;
|
|
28
43
|
target.__onConnected__ = onConnected;
|
|
29
44
|
target.__onDisconnected__ = onDisconnected;
|
|
30
45
|
|
|
31
46
|
const originalConnectedCallback = target.connectedCallback;
|
|
32
47
|
target.connectedCallback = function (this: any) {
|
|
48
|
+
if (this.__beforeConnectedCallbackCalls__) {
|
|
49
|
+
this.__beforeConnectedCallbackCalls__.forEach(
|
|
50
|
+
(callback: ConnectedCallback) => callback(this),
|
|
51
|
+
);
|
|
52
|
+
}
|
|
33
53
|
originalConnectedCallback?.call(this);
|
|
34
54
|
if (this.__connectedCallbackCalls__) {
|
|
35
55
|
this.__connectedCallbackCalls__.forEach((callback: ConnectedCallback) =>
|
|
36
|
-
callback(this)
|
|
56
|
+
callback(this),
|
|
37
57
|
);
|
|
38
58
|
}
|
|
39
59
|
};
|
|
@@ -43,7 +63,7 @@ export function setSubscribable(target: any) {
|
|
|
43
63
|
originalDisconnectedCallback?.call(this);
|
|
44
64
|
if (this.__disconnectedCallbackCalls__) {
|
|
45
65
|
this.__disconnectedCallbackCalls__.forEach(
|
|
46
|
-
(callback: ConnectedCallback) => callback(this)
|
|
66
|
+
(callback: ConnectedCallback) => callback(this),
|
|
47
67
|
);
|
|
48
68
|
}
|
|
49
69
|
};
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { observeDynamicProperty } from "./dynamicPropertyWatch";
|
|
3
|
+
|
|
4
|
+
const legacyStoreKey = Symbol("legacyStore");
|
|
5
|
+
const legacyHookedKey = Symbol("legacyHooked");
|
|
6
|
+
|
|
7
|
+
async function flushFrames(count = 2): Promise<void> {
|
|
8
|
+
for (let i = 0; i < count; i += 1) {
|
|
9
|
+
await new Promise<void>((resolve) => {
|
|
10
|
+
requestAnimationFrame(() => resolve());
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
describe("observeDynamicProperty (poll rAF)", () => {
|
|
16
|
+
afterEach(() => {
|
|
17
|
+
vi.restoreAllMocks();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("déclenche le handler quand une prop observée change", async () => {
|
|
21
|
+
const component = { userIndex: 0 };
|
|
22
|
+
const onDependencyChange = vi.fn();
|
|
23
|
+
|
|
24
|
+
observeDynamicProperty(
|
|
25
|
+
legacyStoreKey,
|
|
26
|
+
legacyHookedKey,
|
|
27
|
+
component,
|
|
28
|
+
"userIndex",
|
|
29
|
+
onDependencyChange,
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
component.userIndex = 1;
|
|
33
|
+
await flushFrames();
|
|
34
|
+
|
|
35
|
+
expect(onDependencyChange).toHaveBeenCalled();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("ne déclenche pas sans changement de valeur", async () => {
|
|
39
|
+
const component = { userIndex: 0 };
|
|
40
|
+
const onDependencyChange = vi.fn();
|
|
41
|
+
|
|
42
|
+
observeDynamicProperty(
|
|
43
|
+
legacyStoreKey,
|
|
44
|
+
legacyHookedKey,
|
|
45
|
+
component,
|
|
46
|
+
"userIndex",
|
|
47
|
+
onDependencyChange,
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
await flushFrames();
|
|
51
|
+
|
|
52
|
+
expect(onDependencyChange).not.toHaveBeenCalled();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("propage un changement effectué dans le handler (même frame)", async () => {
|
|
56
|
+
const component = { a: 0, b: 0 };
|
|
57
|
+
const onAChange = vi.fn(() => {
|
|
58
|
+
component.b = 1;
|
|
59
|
+
});
|
|
60
|
+
const onBChange = vi.fn();
|
|
61
|
+
|
|
62
|
+
observeDynamicProperty(
|
|
63
|
+
legacyStoreKey,
|
|
64
|
+
legacyHookedKey,
|
|
65
|
+
component,
|
|
66
|
+
"a",
|
|
67
|
+
onAChange,
|
|
68
|
+
);
|
|
69
|
+
observeDynamicProperty(
|
|
70
|
+
legacyStoreKey,
|
|
71
|
+
legacyHookedKey,
|
|
72
|
+
component,
|
|
73
|
+
"b",
|
|
74
|
+
onBChange,
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
component.a = 1;
|
|
78
|
+
await flushFrames();
|
|
79
|
+
|
|
80
|
+
expect(onAChange).toHaveBeenCalled();
|
|
81
|
+
expect(onBChange).toHaveBeenCalled();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("désinscription : plus de handler après cleanup", async () => {
|
|
85
|
+
const component = { userIndex: 0 };
|
|
86
|
+
const onDependencyChange = vi.fn();
|
|
87
|
+
|
|
88
|
+
const unobserve = observeDynamicProperty(
|
|
89
|
+
legacyStoreKey,
|
|
90
|
+
legacyHookedKey,
|
|
91
|
+
component,
|
|
92
|
+
"userIndex",
|
|
93
|
+
onDependencyChange,
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
unobserve();
|
|
97
|
+
component.userIndex = 2;
|
|
98
|
+
await flushFrames();
|
|
99
|
+
|
|
100
|
+
expect(onDependencyChange).not.toHaveBeenCalled();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("alerte si boucle infinie (handler qui remute la même prop)", async () => {
|
|
104
|
+
const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
105
|
+
const component = { loop: 0 };
|
|
106
|
+
const onDependencyChange = vi.fn(() => {
|
|
107
|
+
component.loop += 1;
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
observeDynamicProperty(
|
|
111
|
+
legacyStoreKey,
|
|
112
|
+
legacyHookedKey,
|
|
113
|
+
component,
|
|
114
|
+
"loop",
|
|
115
|
+
onDependencyChange,
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
component.loop = 1;
|
|
119
|
+
await flushFrames(3);
|
|
120
|
+
|
|
121
|
+
expect(warn).toHaveBeenCalledWith(
|
|
122
|
+
expect.stringContaining("boucle infinie"),
|
|
123
|
+
);
|
|
124
|
+
});
|
|
125
|
+
});
|