@supersoniks/concorde 3.2.6 → 3.3.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.
Files changed (52) hide show
  1. package/build-infos.json +1 -1
  2. package/concorde-core.bundle.js +249 -249
  3. package/concorde-core.es.js +2251 -1886
  4. package/dist/concorde-core.bundle.js +249 -249
  5. package/dist/concorde-core.es.js +2251 -1886
  6. package/docs/assets/{index-C0K6xugr.css → index-B669R8JF.css} +1 -1
  7. package/docs/assets/index-BTo6ly4d.js +4820 -0
  8. package/docs/index.html +2 -2
  9. package/docs/src/core/components/functional/fetch/fetch.md +6 -0
  10. package/docs/src/core/components/ui/menu/menu.md +46 -5
  11. package/docs/src/core/components/ui/modal/modal.md +0 -4
  12. package/docs/src/core/components/ui/toast/toast.md +166 -0
  13. package/docs/src/docs/_misc/ancestor-attribute.md +94 -0
  14. package/docs/src/docs/_misc/auto-subscribe.md +199 -0
  15. package/docs/src/docs/_misc/bind.md +362 -0
  16. package/docs/src/docs/_misc/on-assign.md +336 -0
  17. package/docs/src/docs/_misc/templates-demo.md +19 -0
  18. package/docs/src/docs/search/docs-search.json +550 -0
  19. package/docs/src/tsconfig-model.json +1 -1
  20. package/docs/src/tsconfig.json +28 -8
  21. package/package.json +8 -1
  22. package/src/core/components/functional/queue/queue.demo.ts +8 -11
  23. package/src/core/components/functional/sdui/sdui.ts +47 -22
  24. package/src/core/components/ui/form/input-autocomplete/input-autocomplete.ts +9 -1
  25. package/src/core/components/ui/tooltip/tooltip.ts +67 -2
  26. package/src/core/decorators/Subscriber.ts +5 -187
  27. package/src/core/decorators/subscriber/ancestorAttribute.ts +17 -0
  28. package/src/core/decorators/subscriber/autoFill.ts +28 -0
  29. package/src/core/decorators/subscriber/autoSubscribe.ts +54 -0
  30. package/src/core/decorators/subscriber/bind.ts +305 -0
  31. package/src/core/decorators/subscriber/common.ts +50 -0
  32. package/src/core/decorators/subscriber/onAssign.ts +318 -0
  33. package/src/core/mixins/Fetcher.ts +17 -8
  34. package/src/core/utils/HTML.ts +2 -0
  35. package/src/core/utils/PublisherProxy.ts +1 -1
  36. package/src/core/utils/api.ts +7 -0
  37. package/src/decorators.ts +9 -2
  38. package/src/docs/_misc/ancestor-attribute.md +94 -0
  39. package/src/docs/_misc/auto-subscribe.md +199 -0
  40. package/src/docs/_misc/bind.md +362 -0
  41. package/src/docs/_misc/on-assign.md +336 -0
  42. package/src/docs/_misc/templates-demo.md +19 -0
  43. package/src/docs/example/decorators-demo.ts +658 -0
  44. package/src/docs/navigation/navigation.ts +22 -3
  45. package/src/docs/search/docs-search.json +415 -0
  46. package/src/docs.ts +4 -0
  47. package/src/tsconfig-model.json +1 -1
  48. package/src/tsconfig.json +22 -2
  49. package/src/tsconfig.tsbuildinfo +1 -1
  50. package/vite.config.mts +0 -2
  51. package/docs/assets/index-Dgl1lJQo.js +0 -4861
  52. package/templates-test.html +0 -32
@@ -0,0 +1,305 @@
1
+ import { Objects } from "@supersoniks/concorde/utils";
2
+
3
+ import { PublisherProxy, PublisherManager } from "../../utils/PublisherProxy";
4
+ import { ConnectedComponent, setSubscribable } from "./common";
5
+
6
+ const dynamicWatcherStore = Symbol("__bindDynamicWatcherStore__");
7
+ const dynamicWillUpdateHookedStore = Symbol("__bindDynamicWillUpdateHooked__");
8
+
9
+ function registerDynamicWatcher(
10
+ instance: any,
11
+ propertyName: string,
12
+ onChange: () => void
13
+ ) {
14
+ const key = String(propertyName);
15
+ ensureWillUpdateHook(instance);
16
+ if (!instance[dynamicWatcherStore]) {
17
+ Object.defineProperty(instance, dynamicWatcherStore, {
18
+ value: new Map<string, Set<() => void>>(),
19
+ enumerable: false,
20
+ configurable: false,
21
+ writable: false,
22
+ });
23
+ }
24
+ const watcherMap = instance[dynamicWatcherStore] as Map<
25
+ string,
26
+ Set<() => void>
27
+ >;
28
+ if (!watcherMap.has(key)) {
29
+ watcherMap.set(key, new Set());
30
+ }
31
+ const watchers = watcherMap.get(key)!;
32
+ watchers.add(onChange);
33
+ return () => {
34
+ watchers.delete(onChange);
35
+ if (watchers.size === 0) {
36
+ watcherMap.delete(key);
37
+ }
38
+ };
39
+ }
40
+
41
+ function ensureWillUpdateHook(instance: any) {
42
+ const proto = Object.getPrototypeOf(instance);
43
+ if (!proto || proto[dynamicWillUpdateHookedStore]) return;
44
+ const originalWillUpdate = Object.prototype.hasOwnProperty.call(
45
+ proto,
46
+ "willUpdate"
47
+ )
48
+ ? proto.willUpdate
49
+ : Object.getPrototypeOf(proto)?.willUpdate;
50
+ proto.willUpdate = function (changedProperties?: Map<unknown, unknown>) {
51
+ const handlers = this[dynamicWatcherStore] as
52
+ | Map<string, Set<() => void>>
53
+ | undefined;
54
+ if (handlers && handlers.size > 0) {
55
+ if (changedProperties && changedProperties.size > 0) {
56
+ changedProperties.forEach((_value, dependency) => {
57
+ const callbacks = handlers.get(String(dependency));
58
+ if (callbacks) {
59
+ callbacks.forEach((cb) => cb());
60
+ }
61
+ });
62
+ } else {
63
+ handlers.forEach((callbacks) => callbacks.forEach((cb) => cb()));
64
+ }
65
+ }
66
+ originalWillUpdate?.call(this, changedProperties);
67
+ };
68
+ proto[dynamicWillUpdateHookedStore] = true;
69
+ }
70
+
71
+ function extractDynamicDependencies(path: string) {
72
+ const patterns = [/\$\{([^}]+)\}/g, /\{\$([^}]+)\}/g];
73
+ const deps = new Set<string>();
74
+ for (const pattern of patterns) {
75
+ let match;
76
+ while ((match = pattern.exec(path)) !== null) {
77
+ const cleaned = cleanPlaceholder(match[1]);
78
+ if (!cleaned) continue;
79
+ const [root] = cleaned.split(".");
80
+ if (root) deps.add(root);
81
+ }
82
+ }
83
+ return Array.from(deps);
84
+ }
85
+
86
+ function cleanPlaceholder(value: string) {
87
+ return value.trim().replace(/^this\./, "");
88
+ }
89
+
90
+ function resolveDynamicPath(component: any, template: string) {
91
+ let missing = false;
92
+ const replaceValue = (_match: string, expression: string) => {
93
+ const cleaned = cleanPlaceholder(expression);
94
+ const resolved = getValueFromExpression(component, cleaned);
95
+ if (resolved === undefined || resolved === null) {
96
+ missing = true;
97
+ return "";
98
+ }
99
+ return `${resolved}`;
100
+ };
101
+ const resolvedPath = template
102
+ .replace(/\$\{([^}]+)\}/g, replaceValue)
103
+ .replace(/\{\$([^}]+)\}/g, replaceValue)
104
+ .trim();
105
+ if (missing || !resolvedPath.length) {
106
+ return { ready: false, path: null };
107
+ }
108
+ const segments = resolvedPath.split(".").filter(Boolean);
109
+ if (segments.length === 0 || !segments[0]) {
110
+ return { ready: false, path: null };
111
+ }
112
+ return { ready: true, path: resolvedPath };
113
+ }
114
+
115
+ function getValueFromExpression(component: any, expression: string) {
116
+ if (!expression) return undefined;
117
+ const segments = expression.split(".").filter(Boolean);
118
+ if (segments.length === 0) return undefined;
119
+ let current: unknown = component;
120
+ for (const segment of segments) {
121
+ if (
122
+ current === undefined ||
123
+ current === null ||
124
+ typeof current !== "object"
125
+ ) {
126
+ return undefined;
127
+ }
128
+ current = (current as Record<string, unknown>)[segment];
129
+ }
130
+ return current;
131
+ }
132
+
133
+ function getPublisherFromPath(path: string) {
134
+ const segments = path.split(".").filter((segment) => segment.length > 0);
135
+ if (segments.length === 0) return null;
136
+ const dataProvider = segments.shift() || "";
137
+ if (!dataProvider) return null;
138
+ let publisher = PublisherManager.get(dataProvider);
139
+ if (!publisher) return null;
140
+ publisher = Objects.traverse(publisher, segments);
141
+ return publisher as PublisherProxy | null;
142
+ }
143
+
144
+ export function bind(path: string, options?: { reflect?: boolean }) {
145
+ const reflect = options?.reflect ?? false;
146
+ const dynamicDependencies = extractDynamicDependencies(path);
147
+ const isDynamicPath = dynamicDependencies.length > 0;
148
+
149
+ return function (target: unknown, propertyKey: string) {
150
+ if (!target) return;
151
+ setSubscribable(target);
152
+ const stateKey = `__bind_state_${propertyKey}`;
153
+ const publisherKey = `__bind_${propertyKey}_publisher__`;
154
+ const isUpdatingFromPublisherKey = reflect
155
+ ? `__bind_${propertyKey}_updating_from_publisher__`
156
+ : null;
157
+
158
+ if (reflect) {
159
+ const existingDescriptor = Object.getOwnPropertyDescriptor(
160
+ target as any,
161
+ propertyKey
162
+ );
163
+ const internalValueKey = `__bind_${propertyKey}_value__`;
164
+ const reflectUpdateFlagKey = `__bind_${propertyKey}_updating_from_publisher__`;
165
+ const initialValue =
166
+ existingDescriptor && !existingDescriptor.get && !existingDescriptor.set
167
+ ? existingDescriptor.value
168
+ : undefined;
169
+
170
+ Object.defineProperty(target as any, propertyKey, {
171
+ get() {
172
+ if (existingDescriptor?.get) {
173
+ return existingDescriptor.get.call(this);
174
+ }
175
+ if (
176
+ !Object.prototype.hasOwnProperty.call(this, internalValueKey) &&
177
+ initialValue !== undefined
178
+ ) {
179
+ (this as any)[internalValueKey] = initialValue;
180
+ }
181
+ return (this as any)[internalValueKey];
182
+ },
183
+ set(newValue: unknown) {
184
+ if (existingDescriptor?.set) {
185
+ existingDescriptor.set.call(this, newValue);
186
+ } else {
187
+ (this as any)[internalValueKey] = newValue;
188
+ }
189
+ if (
190
+ !(this as any)[reflectUpdateFlagKey] &&
191
+ (this as any)[publisherKey]
192
+ ) {
193
+ (this as any)[publisherKey].set(newValue);
194
+ }
195
+ },
196
+ enumerable: existingDescriptor?.enumerable ?? true,
197
+ configurable: existingDescriptor?.configurable ?? true,
198
+ });
199
+ }
200
+
201
+ (target as ConnectedComponent).__onConnected__((component) => {
202
+ const state =
203
+ (component as any)[stateKey] ||
204
+ ((component as any)[stateKey] = {
205
+ cleanupWatchers: [] as Array<() => void>,
206
+ unsubscribePublisher: null as null | (() => void),
207
+ currentPath: null as string | null,
208
+ });
209
+
210
+ if (state.unsubscribePublisher) {
211
+ state.unsubscribePublisher();
212
+ state.unsubscribePublisher = null;
213
+ }
214
+ state.cleanupWatchers.forEach((cleanup: () => void) => cleanup());
215
+ state.cleanupWatchers = [];
216
+ state.currentPath = null;
217
+
218
+ const subscribeToPath = (resolvedPath: string | null) => {
219
+ if (!resolvedPath) {
220
+ if (state.unsubscribePublisher) {
221
+ state.unsubscribePublisher();
222
+ state.unsubscribePublisher = null;
223
+ }
224
+ state.currentPath = null;
225
+ (component as any)[publisherKey] = null;
226
+ return;
227
+ }
228
+ if (resolvedPath === state.currentPath) {
229
+ return;
230
+ }
231
+ if (state.unsubscribePublisher) {
232
+ state.unsubscribePublisher();
233
+ state.unsubscribePublisher = null;
234
+ }
235
+
236
+ const publisher = getPublisherFromPath(resolvedPath);
237
+ if (!publisher) {
238
+ state.currentPath = null;
239
+ (component as any)[publisherKey] = null;
240
+ return;
241
+ }
242
+
243
+ const onAssign = (value: unknown) => {
244
+ if (reflect && isUpdatingFromPublisherKey) {
245
+ (component as any)[isUpdatingFromPublisherKey] = true;
246
+ }
247
+ component[propertyKey] = value;
248
+ if (reflect && isUpdatingFromPublisherKey) {
249
+ (component as any)[isUpdatingFromPublisherKey] = false;
250
+ }
251
+ };
252
+
253
+ publisher.onAssign(onAssign);
254
+ state.unsubscribePublisher = () => {
255
+ publisher.offAssign(onAssign);
256
+ if ((component as any)[publisherKey] === publisher) {
257
+ (component as any)[publisherKey] = null;
258
+ }
259
+ };
260
+ state.currentPath = resolvedPath;
261
+ (component as any)[publisherKey] = publisher;
262
+ };
263
+
264
+ const refreshSubscription = () => {
265
+ if (isDynamicPath) {
266
+ const resolution = resolveDynamicPath(component, path);
267
+ if (!resolution.ready) {
268
+ subscribeToPath(null);
269
+ return;
270
+ }
271
+ subscribeToPath(resolution.path);
272
+ return;
273
+ }
274
+ subscribeToPath(path);
275
+ };
276
+
277
+ if (isDynamicPath) {
278
+ for (const dependency of dynamicDependencies) {
279
+ const unsubscribe = registerDynamicWatcher(
280
+ component as Record<string, unknown>,
281
+ dependency,
282
+ () => refreshSubscription()
283
+ );
284
+ state.cleanupWatchers.push(unsubscribe);
285
+ }
286
+ }
287
+
288
+ refreshSubscription();
289
+ });
290
+
291
+ (target as ConnectedComponent).__onDisconnected__((component) => {
292
+ const state = (component as any)[stateKey];
293
+ if (!state) return;
294
+ if (state.unsubscribePublisher) {
295
+ state.unsubscribePublisher();
296
+ state.unsubscribePublisher = null;
297
+ }
298
+ state.cleanupWatchers.forEach((cleanup: () => void) => cleanup());
299
+ state.cleanupWatchers = [];
300
+ state.currentPath = null;
301
+ (component as any)[publisherKey] = null;
302
+ });
303
+ };
304
+ }
305
+
@@ -0,0 +1,50 @@
1
+ type ConnectedCallback = (component: ConnectedComponent) => void;
2
+
3
+ export type ConnectedComponent = Record<string, unknown> & {
4
+ __onConnected__: (callback: ConnectedCallback) => void;
5
+ __onDisconnected__: (callback: ConnectedCallback) => void;
6
+ __connectedCallbackCalls__?: Set<ConnectedCallback>;
7
+ __disconnectedCallbackCalls__?: Set<ConnectedCallback>;
8
+ };
9
+
10
+ function onConnected(this: ConnectedComponent, callback: ConnectedCallback) {
11
+ if (!this.__connectedCallbackCalls__) {
12
+ this.__connectedCallbackCalls__ = new Set();
13
+ }
14
+ this.__connectedCallbackCalls__.add(callback);
15
+ }
16
+
17
+ function onDisconnected(this: ConnectedComponent, callback: ConnectedCallback) {
18
+ if (!this.__disconnectedCallbackCalls__) {
19
+ this.__disconnectedCallbackCalls__ = new Set();
20
+ }
21
+ this.__disconnectedCallbackCalls__.add(callback);
22
+ }
23
+
24
+ export function setSubscribable(target: any) {
25
+ if (target.__is__setSubscribable__) return;
26
+ target.__is__setSubscribable__ = true;
27
+
28
+ target.__onConnected__ = onConnected;
29
+ target.__onDisconnected__ = onDisconnected;
30
+
31
+ const originalConnectedCallback = target.connectedCallback;
32
+ target.connectedCallback = function (this: any) {
33
+ originalConnectedCallback?.call(this);
34
+ if (this.__connectedCallbackCalls__) {
35
+ this.__connectedCallbackCalls__.forEach((callback: ConnectedCallback) =>
36
+ callback(this)
37
+ );
38
+ }
39
+ };
40
+
41
+ const originalDisconnectedCallback = target.disconnectedCallback;
42
+ target.disconnectedCallback = function (this: any) {
43
+ originalDisconnectedCallback?.call(this);
44
+ if (this.__disconnectedCallbackCalls__) {
45
+ this.__disconnectedCallbackCalls__.forEach(
46
+ (callback: ConnectedCallback) => callback(this)
47
+ );
48
+ }
49
+ };
50
+ }
@@ -0,0 +1,318 @@
1
+ import { Objects } from "@supersoniks/concorde/utils";
2
+
3
+ import { PublisherProxy, PublisherManager } from "../../utils/PublisherProxy";
4
+ import { ConnectedComponent, setSubscribable } from "./common";
5
+
6
+ const dynamicWatcherStore = Symbol("__onAssignDynamicWatcherStore__");
7
+ const dynamicWillUpdateHookedStore = Symbol(
8
+ "__onAssignDynamicWillUpdateHooked__"
9
+ );
10
+
11
+ function registerDynamicWatcher(
12
+ instance: any,
13
+ propertyName: string,
14
+ onChange: () => void
15
+ ) {
16
+ const key = String(propertyName);
17
+ ensureWillUpdateHook(instance);
18
+ if (!instance[dynamicWatcherStore]) {
19
+ Object.defineProperty(instance, dynamicWatcherStore, {
20
+ value: new Map<string, Set<() => void>>(),
21
+ enumerable: false,
22
+ configurable: false,
23
+ writable: false,
24
+ });
25
+ }
26
+ const watcherMap = instance[dynamicWatcherStore] as Map<
27
+ string,
28
+ Set<() => void>
29
+ >;
30
+ if (!watcherMap.has(key)) {
31
+ watcherMap.set(key, new Set());
32
+ }
33
+ const watchers = watcherMap.get(key)!;
34
+ watchers.add(onChange);
35
+ return () => {
36
+ watchers.delete(onChange);
37
+ if (watchers.size === 0) {
38
+ watcherMap.delete(key);
39
+ }
40
+ };
41
+ }
42
+
43
+ function ensureWillUpdateHook(instance: any) {
44
+ const proto = Object.getPrototypeOf(instance);
45
+ if (!proto || proto[dynamicWillUpdateHookedStore]) return;
46
+ const originalWillUpdate = Object.prototype.hasOwnProperty.call(
47
+ proto,
48
+ "willUpdate"
49
+ )
50
+ ? proto.willUpdate
51
+ : Object.getPrototypeOf(proto)?.willUpdate;
52
+ proto.willUpdate = function (changedProperties?: Map<unknown, unknown>) {
53
+ const handlers = this[dynamicWatcherStore] as
54
+ | Map<string, Set<() => void>>
55
+ | undefined;
56
+ if (handlers && handlers.size > 0) {
57
+ if (changedProperties && changedProperties.size > 0) {
58
+ changedProperties.forEach((_value, dependency) => {
59
+ const callbacks = handlers.get(String(dependency));
60
+ if (callbacks) {
61
+ callbacks.forEach((cb) => cb());
62
+ }
63
+ });
64
+ } else {
65
+ handlers.forEach((callbacks) => callbacks.forEach((cb) => cb()));
66
+ }
67
+ }
68
+ originalWillUpdate?.call(this, changedProperties);
69
+ };
70
+ proto[dynamicWillUpdateHookedStore] = true;
71
+ }
72
+
73
+ function extractDynamicDependencies(path: string) {
74
+ const patterns = [/\$\{([^}]+)\}/g, /\{\$([^}]+)\}/g];
75
+ const deps = new Set<string>();
76
+ for (const pattern of patterns) {
77
+ let match;
78
+ while ((match = pattern.exec(path)) !== null) {
79
+ const cleaned = cleanPlaceholder(match[1]);
80
+ if (!cleaned) continue;
81
+ const [root] = cleaned.split(".");
82
+ if (root) deps.add(root);
83
+ }
84
+ }
85
+ return Array.from(deps);
86
+ }
87
+
88
+ function cleanPlaceholder(value: string) {
89
+ return value.trim().replace(/^this\./, "");
90
+ }
91
+
92
+ function resolveDynamicPath(component: any, template: string) {
93
+ let missing = false;
94
+ const replaceValue = (_match: string, expression: string) => {
95
+ const cleaned = cleanPlaceholder(expression);
96
+ const resolved = getValueFromExpression(component, cleaned);
97
+ if (resolved === undefined || resolved === null) {
98
+ missing = true;
99
+ return "";
100
+ }
101
+ return `${resolved}`;
102
+ };
103
+ const resolvedPath = template
104
+ .replace(/\$\{([^}]+)\}/g, replaceValue)
105
+ .replace(/\{\$([^}]+)\}/g, replaceValue)
106
+ .trim();
107
+ if (missing || !resolvedPath.length) {
108
+ return { ready: false, path: null };
109
+ }
110
+ const segments = resolvedPath.split(".").filter(Boolean);
111
+ if (segments.length === 0 || !segments[0]) {
112
+ return { ready: false, path: null };
113
+ }
114
+ return { ready: true, path: resolvedPath };
115
+ }
116
+
117
+ function getValueFromExpression(component: any, expression: string) {
118
+ if (!expression) return undefined;
119
+ const segments = expression.split(".").filter(Boolean);
120
+ if (segments.length === 0) return undefined;
121
+ let current: unknown = component;
122
+ for (const segment of segments) {
123
+ if (
124
+ current === undefined ||
125
+ current === null ||
126
+ typeof current !== "object"
127
+ ) {
128
+ return undefined;
129
+ }
130
+ current = (current as Record<string, unknown>)[segment];
131
+ }
132
+ return current;
133
+ }
134
+
135
+ function getPublisherFromPath(path: string) {
136
+ const segments = path.split(".").filter((segment) => segment.length > 0);
137
+ if (segments.length === 0) return null;
138
+ const dataProvider = segments.shift() || "";
139
+ if (!dataProvider) return null;
140
+ let publisher = PublisherManager.get(dataProvider);
141
+ if (!publisher) return null;
142
+ publisher = Objects.traverse(publisher, segments);
143
+ return publisher as PublisherProxy | null;
144
+ }
145
+
146
+ type Callback = (...values: unknown[]) => void;
147
+ type PathConfiguration = {
148
+ originalPath: string;
149
+ dynamicDependencies: string[];
150
+ isDynamic: boolean;
151
+ };
152
+
153
+ type Configuration = {
154
+ callbacks: Set<Callback>;
155
+ publisher: PublisherProxy | null;
156
+ onAssign: (value: unknown) => void;
157
+ unsubscribePublisher: (() => void) | null;
158
+ pathConfig: PathConfiguration;
159
+ index: number;
160
+ };
161
+
162
+ export function onAssign(...values: Array<string>) {
163
+ const pathConfigs: PathConfiguration[] = values.map((path) => {
164
+ const dynamicDependencies = extractDynamicDependencies(path);
165
+ return {
166
+ originalPath: path,
167
+ dynamicDependencies,
168
+ isDynamic: dynamicDependencies.length > 0,
169
+ };
170
+ });
171
+
172
+ return function (
173
+ target: unknown,
174
+ _propertyKey: string,
175
+ descriptor: PropertyDescriptor
176
+ ) {
177
+ setSubscribable(target);
178
+ const stateKey = `__onAssign_state__`;
179
+ let callback: Callback;
180
+
181
+ (target as ConnectedComponent).__onConnected__((component) => {
182
+ const state =
183
+ (component as any)[stateKey] ||
184
+ ((component as any)[stateKey] = {
185
+ cleanupWatchers: [] as Array<() => void>,
186
+ configurations: [] as Configuration[],
187
+ });
188
+
189
+ // Nettoyage des watchers et configurations précédentes
190
+ state.cleanupWatchers.forEach((cleanup: () => void) => cleanup());
191
+ state.cleanupWatchers = [];
192
+ state.configurations.forEach((conf: Configuration) => {
193
+ if (conf.unsubscribePublisher) {
194
+ conf.unsubscribePublisher();
195
+ }
196
+ });
197
+ state.configurations = [];
198
+
199
+ const onAssignValues: unknown[] = [];
200
+ const confs: Configuration[] = [];
201
+
202
+ // Initialisation des configurations
203
+ for (let i = 0; i < values.length; i++) {
204
+ const pathConfig = pathConfigs[i];
205
+ const callbacks: Set<Callback> = new Set();
206
+ const onAssign = (assignedValue: unknown) => {
207
+ onAssignValues[i] = assignedValue;
208
+ if (
209
+ onAssignValues.filter((v) => v !== null && v !== undefined)
210
+ .length === values.length
211
+ ) {
212
+ callbacks.forEach((callback) => callback(...onAssignValues));
213
+ }
214
+ };
215
+ confs.push({
216
+ publisher: null,
217
+ onAssign,
218
+ callbacks,
219
+ unsubscribePublisher: null,
220
+ pathConfig,
221
+ index: i,
222
+ });
223
+ }
224
+
225
+ const subscribeToPath = (
226
+ conf: Configuration,
227
+ resolvedPath: string | null
228
+ ) => {
229
+ // Désabonnement de l'ancien publisher
230
+ if (conf.unsubscribePublisher) {
231
+ conf.unsubscribePublisher();
232
+ conf.unsubscribePublisher = null;
233
+ }
234
+
235
+ // Réinitialiser la valeur pour ce chemin lors du changement
236
+ onAssignValues[conf.index] = null;
237
+ conf.publisher = null;
238
+
239
+ if (!resolvedPath) {
240
+ return;
241
+ }
242
+
243
+ const publisher = getPublisherFromPath(resolvedPath);
244
+ if (!publisher) {
245
+ return;
246
+ }
247
+
248
+ publisher.onAssign(conf.onAssign);
249
+ conf.unsubscribePublisher = () => {
250
+ publisher.offAssign(conf.onAssign);
251
+ if (conf.publisher === publisher) {
252
+ conf.publisher = null;
253
+ }
254
+ };
255
+ conf.publisher = publisher;
256
+ };
257
+
258
+ const refreshSubscriptions = () => {
259
+ for (const conf of confs) {
260
+ if (conf.pathConfig.isDynamic) {
261
+ const resolution = resolveDynamicPath(
262
+ component,
263
+ conf.pathConfig.originalPath
264
+ );
265
+ if (!resolution.ready) {
266
+ subscribeToPath(conf, null);
267
+ continue;
268
+ }
269
+ subscribeToPath(conf, resolution.path);
270
+ } else {
271
+ subscribeToPath(conf, conf.pathConfig.originalPath);
272
+ }
273
+ }
274
+ };
275
+
276
+ // Enregistrement des watchers pour les chemins dynamiques
277
+ for (const conf of confs) {
278
+ if (conf.pathConfig.isDynamic) {
279
+ for (const dependency of conf.pathConfig.dynamicDependencies) {
280
+ const unsubscribe = registerDynamicWatcher(
281
+ component as Record<string, unknown>,
282
+ dependency,
283
+ () => refreshSubscriptions()
284
+ );
285
+ state.cleanupWatchers.push(unsubscribe);
286
+ }
287
+ }
288
+ }
289
+
290
+ // Initialisation du callback
291
+ callback = descriptor.value.bind(component);
292
+ for (const conf of confs) {
293
+ conf.callbacks.add(callback);
294
+ }
295
+
296
+ // Initialisation des abonnements
297
+ refreshSubscriptions();
298
+
299
+ state.configurations = confs;
300
+ });
301
+
302
+ (target as ConnectedComponent).__onDisconnected__((component) => {
303
+ const state = (component as any)[stateKey];
304
+ if (!state) return;
305
+
306
+ state.cleanupWatchers.forEach((cleanup: () => void) => cleanup());
307
+ state.cleanupWatchers = [];
308
+
309
+ state.configurations.forEach((conf: Configuration) => {
310
+ if (conf.unsubscribePublisher) {
311
+ conf.unsubscribePublisher();
312
+ }
313
+ conf.callbacks.delete(callback);
314
+ });
315
+ state.configurations = [];
316
+ });
317
+ };
318
+ }
@@ -121,21 +121,30 @@ const Fetcher = <
121
121
  @property({ type: Number }) refetchEveryMs = 0;
122
122
  refetchTimeOutId?: ReturnType<typeof setTimeout>;
123
123
 
124
- private isFetching = false;
125
- private mustRefetch = false;
124
+ /**
125
+ * _isFetching est une propriété sensée privée qui permet de savoir si un fetch est en cours
126
+ * Elle ne peut pas etre veritablement privée actuellement en raison d'une limitation contextuelle a traiter
127
+ */
128
+ _isFetching = false;
129
+
130
+ /**
131
+ * _mustRefetch est une propriété sensée privée qui permet de savoir si un fetch est en cours
132
+ * Elle ne peut pas etre veritablement privée actuellement en raison d'une limitation contextuelle a traiter
133
+ */
134
+ _mustRefetch = false;
126
135
  handleStartFetching(): "fetching" | "okToFetch" {
127
- if (this.isFetching) {
128
- this.mustRefetch = true;
136
+ if (this._isFetching) {
137
+ this._mustRefetch = true;
129
138
  return "fetching";
130
139
  }
131
- this.isFetching = true;
140
+ this._isFetching = true;
132
141
  return "okToFetch";
133
142
  }
134
143
 
135
144
  handleEndFetching() {
136
- this.isFetching = false;
137
- if (this.mustRefetch) {
138
- this.mustRefetch = false;
145
+ this._isFetching = false;
146
+ if (this._mustRefetch) {
147
+ this._mustRefetch = false;
139
148
  this._fetchData();
140
149
  }
141
150
  }