@ama-mfe/ng-utils 13.3.0-prerelease.22 → 13.3.0-prerelease.24
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/fesm2022/ama-mfe-ng-utils.mjs +330 -260
- package/fesm2022/ama-mfe-ng-utils.mjs.map +1 -1
- package/index.d.ts +210 -169
- package/index.d.ts.map +1 -1
- package/package.json +6 -6
|
@@ -1,13 +1,76 @@
|
|
|
1
1
|
import { MessagePeerService, MESSAGE_PEER_CONFIG, MESSAGE_PEER_CONNECT_OPTIONS } from '@amadeus-it-group/microfrontends-angular';
|
|
2
2
|
export { MessagePeerService as ConnectionService } from '@amadeus-it-group/microfrontends-angular';
|
|
3
3
|
import * as i0 from '@angular/core';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { input, inject, ElementRef, computed, SecurityContext, effect, HostBinding, Directive, Injectable, signal, DestroyRef, provideAppInitializer, Pipe, makeEnvironmentProviders, untracked, afterNextRender, Renderer2 } from '@angular/core';
|
|
5
|
+
import { DomSanitizer } from '@angular/platform-browser';
|
|
6
6
|
import { LoggerService } from '@o3r/logger';
|
|
7
|
-
import {
|
|
7
|
+
import { HISTORY_MESSAGE_TYPE, NAVIGATION_MESSAGE_TYPE, RESIZE_MESSAGE_TYPE, THEME_MESSAGE_TYPE } from '@ama-mfe/messages';
|
|
8
|
+
import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
|
|
8
9
|
import { Router, ActivatedRoute, NavigationEnd } from '@angular/router';
|
|
9
10
|
import { Subject, filter, map } from 'rxjs';
|
|
10
|
-
|
|
11
|
+
|
|
12
|
+
class ConnectDirective {
|
|
13
|
+
/**
|
|
14
|
+
* Binds the `src` attribute of the iframe to the sanitized source URL.
|
|
15
|
+
*/
|
|
16
|
+
get srcAttr() {
|
|
17
|
+
return this.src();
|
|
18
|
+
}
|
|
19
|
+
constructor() {
|
|
20
|
+
/**
|
|
21
|
+
* The connection ID required for the message peer service.
|
|
22
|
+
*/
|
|
23
|
+
this.connect = input.required(...(ngDevMode ? [{ debugName: "connect" }] : []));
|
|
24
|
+
/**
|
|
25
|
+
* The sanitized source URL for the iframe.
|
|
26
|
+
*/
|
|
27
|
+
this.src = input(...(ngDevMode ? [undefined, { debugName: "src" }] : []));
|
|
28
|
+
this.messageService = inject(MessagePeerService);
|
|
29
|
+
this.domSanitizer = inject(DomSanitizer);
|
|
30
|
+
this.iframeElement = inject(ElementRef).nativeElement;
|
|
31
|
+
this.clientOrigin = computed(() => {
|
|
32
|
+
const src = this.src();
|
|
33
|
+
const srcString = src && this.domSanitizer.sanitize(SecurityContext.RESOURCE_URL, src);
|
|
34
|
+
return srcString && new URL(srcString).origin;
|
|
35
|
+
}, ...(ngDevMode ? [{ debugName: "clientOrigin" }] : []));
|
|
36
|
+
const logger = inject(LoggerService);
|
|
37
|
+
// When the origin or connection ID change - reconnect the message service
|
|
38
|
+
effect((onCleanup) => {
|
|
39
|
+
let stopHandshakeListening = () => { };
|
|
40
|
+
const origin = this.clientOrigin();
|
|
41
|
+
const id = this.connect();
|
|
42
|
+
const source = this.iframeElement.contentWindow;
|
|
43
|
+
// listen for handshakes only if we know the origin and were given a connection ID
|
|
44
|
+
if (origin && source && id) {
|
|
45
|
+
try {
|
|
46
|
+
stopHandshakeListening = this.messageService.listen({ id, source, origin });
|
|
47
|
+
}
|
|
48
|
+
catch (e) {
|
|
49
|
+
logger.error(`Failed to start listening for (connection ID: ${id})`, e);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// stop listening for handshakes and disconnect previous connection when:
|
|
53
|
+
// - origin/connection ID change
|
|
54
|
+
// - the directive is destroyed
|
|
55
|
+
onCleanup(() => {
|
|
56
|
+
stopHandshakeListening();
|
|
57
|
+
this.messageService.disconnect();
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: ConnectDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
62
|
+
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.4", type: ConnectDirective, isStandalone: true, selector: "iframe[connect]", inputs: { connect: { classPropertyName: "connect", publicName: "connect", isSignal: true, isRequired: true, transformFunction: null }, src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "src": "this.srcAttr" } }, ngImport: i0 }); }
|
|
63
|
+
}
|
|
64
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: ConnectDirective, decorators: [{
|
|
65
|
+
type: Directive,
|
|
66
|
+
args: [{
|
|
67
|
+
selector: 'iframe[connect]',
|
|
68
|
+
standalone: true
|
|
69
|
+
}]
|
|
70
|
+
}], ctorParameters: () => [], propDecorators: { srcAttr: [{
|
|
71
|
+
type: HostBinding,
|
|
72
|
+
args: ['src']
|
|
73
|
+
}] } });
|
|
11
74
|
|
|
12
75
|
/**
|
|
13
76
|
* Gets the available consumers and formats them into a {@see DeclareMessages} object.
|
|
@@ -210,53 +273,49 @@ const registerConsumer = (consumer) => {
|
|
|
210
273
|
});
|
|
211
274
|
};
|
|
212
275
|
|
|
213
|
-
/** the error message type */
|
|
214
|
-
const ERROR_MESSAGE_TYPE = 'error';
|
|
215
|
-
|
|
216
276
|
/**
|
|
217
|
-
*
|
|
277
|
+
* A service that handles history messages.
|
|
278
|
+
*
|
|
279
|
+
* This service listens for history messages and navigates accordingly.
|
|
218
280
|
*/
|
|
219
|
-
class
|
|
281
|
+
class HistoryConsumerService {
|
|
220
282
|
constructor() {
|
|
221
|
-
this.newHeight = signal(undefined, ...(ngDevMode ? [{ debugName: "newHeight" }] : []));
|
|
222
|
-
/**
|
|
223
|
-
* A readonly signal that provides the new height information from the channel.
|
|
224
|
-
*/
|
|
225
|
-
this.newHeightFromChannel = this.newHeight.asReadonly();
|
|
226
283
|
/**
|
|
227
|
-
* The type of messages this service handles
|
|
284
|
+
* The type of messages this service handles.
|
|
228
285
|
*/
|
|
229
|
-
this.type =
|
|
286
|
+
this.type = HISTORY_MESSAGE_TYPE;
|
|
230
287
|
/**
|
|
231
|
-
*
|
|
288
|
+
* @inheritdoc
|
|
232
289
|
*/
|
|
233
290
|
this.supportedVersions = {
|
|
234
291
|
/**
|
|
235
|
-
* Use the message
|
|
292
|
+
* Use the message payload to navigate in the history
|
|
236
293
|
* @param message message to consume
|
|
237
294
|
*/
|
|
238
|
-
'1.0': (message) =>
|
|
295
|
+
'1.0': (message) => {
|
|
296
|
+
history.go(message.payload.delta);
|
|
297
|
+
}
|
|
239
298
|
};
|
|
240
299
|
this.consumerManagerService = inject(ConsumerManagerService);
|
|
241
300
|
this.start();
|
|
242
301
|
inject(DestroyRef).onDestroy(() => this.stop());
|
|
243
302
|
}
|
|
244
303
|
/**
|
|
245
|
-
*
|
|
304
|
+
* @inheritdoc
|
|
246
305
|
*/
|
|
247
306
|
start() {
|
|
248
307
|
this.consumerManagerService.register(this);
|
|
249
308
|
}
|
|
250
309
|
/**
|
|
251
|
-
*
|
|
310
|
+
* @inheritdoc
|
|
252
311
|
*/
|
|
253
312
|
stop() {
|
|
254
313
|
this.consumerManagerService.unregister(this);
|
|
255
314
|
}
|
|
256
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type:
|
|
257
|
-
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type:
|
|
315
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: HistoryConsumerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
316
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: HistoryConsumerService, providedIn: 'root' }); }
|
|
258
317
|
}
|
|
259
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type:
|
|
318
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: HistoryConsumerService, decorators: [{
|
|
260
319
|
type: Injectable,
|
|
261
320
|
args: [{
|
|
262
321
|
providedIn: 'root'
|
|
@@ -264,102 +323,44 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
|
|
|
264
323
|
}], ctorParameters: () => [] });
|
|
265
324
|
|
|
266
325
|
/**
|
|
267
|
-
*
|
|
268
|
-
*
|
|
326
|
+
* Provides necessary overrides to make the module navigation in history work in an embedded context :
|
|
327
|
+
* - Prevent pushing states to history, replace state instead
|
|
328
|
+
* - Handle history navigation via History messages to let the host manage the states
|
|
269
329
|
*/
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
/**
|
|
287
|
-
* This method sets up a `ResizeObserver` to observe changes in the document's body height.
|
|
288
|
-
* When the height changes, it sends a resize message with the new height, to the connected peers
|
|
289
|
-
*/
|
|
290
|
-
startResizeObserver() {
|
|
291
|
-
this.resizeObserver = new ResizeObserver(() => {
|
|
292
|
-
const newHeight = document.body.getBoundingClientRect().height;
|
|
293
|
-
if (!this.actualHeight || newHeight !== this.actualHeight) {
|
|
294
|
-
this.actualHeight = newHeight;
|
|
295
|
-
const messageV10 = {
|
|
296
|
-
type: 'resize',
|
|
297
|
-
version: '1.0',
|
|
298
|
-
height: this.actualHeight
|
|
299
|
-
};
|
|
300
|
-
// TODO: sendBest() is not implemented -- https://github.com/AmadeusITGroup/microfrontends/issues/11
|
|
301
|
-
this.messageService.send(messageV10);
|
|
302
|
-
}
|
|
330
|
+
function provideHistoryOverrides() {
|
|
331
|
+
return provideAppInitializer(() => {
|
|
332
|
+
const messageService = inject((MessagePeerService));
|
|
333
|
+
const navigate = (delta) => {
|
|
334
|
+
messageService.send({
|
|
335
|
+
type: 'history',
|
|
336
|
+
version: '1.0',
|
|
337
|
+
delta
|
|
338
|
+
});
|
|
339
|
+
};
|
|
340
|
+
Object.defineProperty(history, 'pushState', {
|
|
341
|
+
value: (data, unused, url) => {
|
|
342
|
+
history.replaceState(data, unused, url);
|
|
343
|
+
},
|
|
344
|
+
writable: false,
|
|
345
|
+
configurable: false
|
|
303
346
|
});
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
}
|
|
309
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: ResizeService, decorators: [{
|
|
310
|
-
type: Injectable,
|
|
311
|
-
args: [{
|
|
312
|
-
providedIn: 'root'
|
|
313
|
-
}]
|
|
314
|
-
}], ctorParameters: () => [] });
|
|
315
|
-
|
|
316
|
-
/**
|
|
317
|
-
* A directive that adjusts the height of an element based on resize messages from a specified channel.
|
|
318
|
-
*/
|
|
319
|
-
class ScalableDirective {
|
|
320
|
-
constructor() {
|
|
321
|
-
/**
|
|
322
|
-
* The connection ID for the element, used as channel id backup
|
|
323
|
-
*/
|
|
324
|
-
this.connect = input(...(ngDevMode ? [undefined, { debugName: "connect" }] : []));
|
|
325
|
-
/**
|
|
326
|
-
* The channel id
|
|
327
|
-
*/
|
|
328
|
-
this.scalable = input(...(ngDevMode ? [undefined, { debugName: "scalable" }] : []));
|
|
329
|
-
this.resizeHandler = inject(ResizeConsumerService);
|
|
330
|
-
/**
|
|
331
|
-
* This signal checks if the current channel requesting the resize matches the channel ID from the resize handler.
|
|
332
|
-
* If they match, it returns the new height information; otherwise, it returns undefined.
|
|
333
|
-
*/
|
|
334
|
-
this.newHeightFromChannel = computed(() => {
|
|
335
|
-
const channelAskingResize = this.scalable() || this.connect();
|
|
336
|
-
const newHeightFromChannel = this.resizeHandler.newHeightFromChannel();
|
|
337
|
-
if (channelAskingResize && newHeightFromChannel?.channelId === channelAskingResize) {
|
|
338
|
-
return newHeightFromChannel;
|
|
339
|
-
}
|
|
340
|
-
return undefined;
|
|
341
|
-
}, ...(ngDevMode ? [{ debugName: "newHeightFromChannel" }] : []));
|
|
342
|
-
const elem = inject(ElementRef);
|
|
343
|
-
const renderer = inject(Renderer2);
|
|
344
|
-
this.resizeHandler.start();
|
|
345
|
-
/** When a new height value is received set the height of the host element (in pixels) */
|
|
346
|
-
effect(() => {
|
|
347
|
-
const newHeightFromChannel = this.newHeightFromChannel();
|
|
348
|
-
if (newHeightFromChannel) {
|
|
349
|
-
renderer.setStyle(elem.nativeElement, 'height', `${newHeightFromChannel.height}px`);
|
|
350
|
-
}
|
|
347
|
+
Object.defineProperty(history, 'back', {
|
|
348
|
+
value: () => navigate(-1),
|
|
349
|
+
writable: false,
|
|
350
|
+
configurable: false
|
|
351
351
|
});
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
352
|
+
Object.defineProperty(history, 'forward', {
|
|
353
|
+
value: () => navigate(1),
|
|
354
|
+
writable: false,
|
|
355
|
+
configurable: false
|
|
356
|
+
});
|
|
357
|
+
Object.defineProperty(history, 'go', {
|
|
358
|
+
value: (delta) => navigate(delta),
|
|
359
|
+
writable: false,
|
|
360
|
+
configurable: false
|
|
361
|
+
});
|
|
362
|
+
});
|
|
355
363
|
}
|
|
356
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: ScalableDirective, decorators: [{
|
|
357
|
-
type: Directive,
|
|
358
|
-
args: [{
|
|
359
|
-
selector: '[scalable]',
|
|
360
|
-
standalone: true
|
|
361
|
-
}]
|
|
362
|
-
}], ctorParameters: () => [] });
|
|
363
364
|
|
|
364
365
|
const SESSION_STORAGE_KEY = 'ama-mfe-host-info';
|
|
365
366
|
/**
|
|
@@ -439,6 +440,70 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
|
|
|
439
440
|
}]
|
|
440
441
|
}] });
|
|
441
442
|
|
|
443
|
+
/** the error message type */
|
|
444
|
+
const ERROR_MESSAGE_TYPE = 'error';
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* A constant array of known message types and their versions.
|
|
448
|
+
*/
|
|
449
|
+
const KNOWN_MESSAGES = [
|
|
450
|
+
{
|
|
451
|
+
type: ERROR_MESSAGE_TYPE,
|
|
452
|
+
version: '1.0'
|
|
453
|
+
}
|
|
454
|
+
];
|
|
455
|
+
/**
|
|
456
|
+
* Returns the default options for starting a client endpoint peer connection.
|
|
457
|
+
* As `origin`, it will take the hostURL from {@link getHostInfo} and the `window` will be the parent window.
|
|
458
|
+
*/
|
|
459
|
+
function getDefaultClientEndpointStartOptions() {
|
|
460
|
+
const hostInfo = getHostInfo();
|
|
461
|
+
if (hostInfo.hostURL) {
|
|
462
|
+
return {
|
|
463
|
+
origin: new URL(hostInfo.hostURL).origin,
|
|
464
|
+
window: window.parent
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
return {};
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Return `true` if embedded inside an iframe, `false` otherwise
|
|
471
|
+
*/
|
|
472
|
+
function isEmbedded() {
|
|
473
|
+
return window.top !== window.self;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* Provide the communication protocol connection configuration
|
|
478
|
+
* @param connectionConfigOptions The identifier of the application in the communication protocol ecosystem plus the types of messages able to exchange and a logger object
|
|
479
|
+
*/
|
|
480
|
+
function provideConnection(connectionConfigOptions) {
|
|
481
|
+
persistHostInfo();
|
|
482
|
+
const connectionId = (isEmbedded() && getHostInfo().moduleApplicationId) || connectionConfigOptions?.id;
|
|
483
|
+
if (!connectionId) {
|
|
484
|
+
(connectionConfigOptions?.logger || console).error('An id (moduleId) needs to be provided for the application in order to establish a connection inside the communication protocol');
|
|
485
|
+
return makeEnvironmentProviders([]);
|
|
486
|
+
}
|
|
487
|
+
const config = {
|
|
488
|
+
id: connectionId,
|
|
489
|
+
messageCheckStrategy: 'version',
|
|
490
|
+
knownMessages: [...KNOWN_MESSAGES, ...(connectionConfigOptions?.knownMessages || [])]
|
|
491
|
+
};
|
|
492
|
+
return makeEnvironmentProviders([
|
|
493
|
+
{
|
|
494
|
+
provide: MESSAGE_PEER_CONFIG, useValue: config
|
|
495
|
+
},
|
|
496
|
+
{
|
|
497
|
+
provide: MESSAGE_PEER_CONNECT_OPTIONS, useValue: getDefaultClientEndpointStartOptions()
|
|
498
|
+
},
|
|
499
|
+
{
|
|
500
|
+
// in the case of the ConnectionService will extend the base service 'useExisting' should be used
|
|
501
|
+
provide: MessagePeerService, useClass: MessagePeerService, deps: [MESSAGE_PEER_CONFIG]
|
|
502
|
+
},
|
|
503
|
+
...isEmbedded() ? [provideHistoryOverrides()] : []
|
|
504
|
+
]);
|
|
505
|
+
}
|
|
506
|
+
|
|
442
507
|
/**
|
|
443
508
|
* A service that handles navigation messages and routing.
|
|
444
509
|
*
|
|
@@ -623,36 +688,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
|
|
|
623
688
|
}]
|
|
624
689
|
}], ctorParameters: () => [] });
|
|
625
690
|
|
|
626
|
-
/**
|
|
627
|
-
* A constant array of known message types and their versions.
|
|
628
|
-
*/
|
|
629
|
-
const KNOWN_MESSAGES = [
|
|
630
|
-
{
|
|
631
|
-
type: ERROR_MESSAGE_TYPE,
|
|
632
|
-
version: '1.0'
|
|
633
|
-
}
|
|
634
|
-
];
|
|
635
|
-
/**
|
|
636
|
-
* Returns the default options for starting a client endpoint peer connection.
|
|
637
|
-
* As `origin`, it will take the hostURL from {@link getHostInfo} and the `window` will be the parent window.
|
|
638
|
-
*/
|
|
639
|
-
function getDefaultClientEndpointStartOptions() {
|
|
640
|
-
const hostInfo = getHostInfo();
|
|
641
|
-
if (hostInfo.hostURL) {
|
|
642
|
-
return {
|
|
643
|
-
origin: new URL(hostInfo.hostURL).origin,
|
|
644
|
-
window: window.parent
|
|
645
|
-
};
|
|
646
|
-
}
|
|
647
|
-
return {};
|
|
648
|
-
}
|
|
649
|
-
/**
|
|
650
|
-
* Return `true` if embedded inside an iframe, `false` otherwise
|
|
651
|
-
*/
|
|
652
|
-
function isEmbedded() {
|
|
653
|
-
return window.top !== window.self;
|
|
654
|
-
}
|
|
655
|
-
|
|
656
691
|
/**
|
|
657
692
|
* A service that keeps in sync Router navigation and navigation messages.
|
|
658
693
|
*
|
|
@@ -796,6 +831,154 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
|
|
|
796
831
|
}]
|
|
797
832
|
}] });
|
|
798
833
|
|
|
834
|
+
/**
|
|
835
|
+
* This service listens for resize messages and updates the height of elements based on the received messages.
|
|
836
|
+
*/
|
|
837
|
+
class ResizeConsumerService {
|
|
838
|
+
constructor() {
|
|
839
|
+
this.newHeight = signal(undefined, ...(ngDevMode ? [{ debugName: "newHeight" }] : []));
|
|
840
|
+
/**
|
|
841
|
+
* A readonly signal that provides the new height information from the channel.
|
|
842
|
+
*/
|
|
843
|
+
this.newHeightFromChannel = this.newHeight.asReadonly();
|
|
844
|
+
/**
|
|
845
|
+
* The type of messages this service handles ('resize').
|
|
846
|
+
*/
|
|
847
|
+
this.type = RESIZE_MESSAGE_TYPE;
|
|
848
|
+
/**
|
|
849
|
+
* The supported versions of resize messages and their handlers.
|
|
850
|
+
*/
|
|
851
|
+
this.supportedVersions = {
|
|
852
|
+
/**
|
|
853
|
+
* Use the message paylod to compute a new height and emit it via the public signal
|
|
854
|
+
* @param message message to consume
|
|
855
|
+
*/
|
|
856
|
+
'1.0': (message) => this.newHeight.set({ height: message.payload.height, channelId: message.from })
|
|
857
|
+
};
|
|
858
|
+
this.consumerManagerService = inject(ConsumerManagerService);
|
|
859
|
+
this.start();
|
|
860
|
+
inject(DestroyRef).onDestroy(() => this.stop());
|
|
861
|
+
}
|
|
862
|
+
/**
|
|
863
|
+
* Starts the resize handler service by registering it into the consumer manager service.
|
|
864
|
+
*/
|
|
865
|
+
start() {
|
|
866
|
+
this.consumerManagerService.register(this);
|
|
867
|
+
}
|
|
868
|
+
/**
|
|
869
|
+
* Stops the resize handler service by unregistering it from the consumer manager service.
|
|
870
|
+
*/
|
|
871
|
+
stop() {
|
|
872
|
+
this.consumerManagerService.unregister(this);
|
|
873
|
+
}
|
|
874
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: ResizeConsumerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
875
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: ResizeConsumerService, providedIn: 'root' }); }
|
|
876
|
+
}
|
|
877
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: ResizeConsumerService, decorators: [{
|
|
878
|
+
type: Injectable,
|
|
879
|
+
args: [{
|
|
880
|
+
providedIn: 'root'
|
|
881
|
+
}]
|
|
882
|
+
}], ctorParameters: () => [] });
|
|
883
|
+
|
|
884
|
+
/**
|
|
885
|
+
* This service observe changes in the document's body height.
|
|
886
|
+
* When the height changes, it sends a resize message with the new height, to the connected peers
|
|
887
|
+
*/
|
|
888
|
+
class ResizeService {
|
|
889
|
+
constructor() {
|
|
890
|
+
this.messageService = inject((MessagePeerService));
|
|
891
|
+
/**
|
|
892
|
+
* @inheritdoc
|
|
893
|
+
*/
|
|
894
|
+
this.types = RESIZE_MESSAGE_TYPE;
|
|
895
|
+
registerProducer(this);
|
|
896
|
+
}
|
|
897
|
+
/**
|
|
898
|
+
* @inheritdoc
|
|
899
|
+
*/
|
|
900
|
+
handleError(message) {
|
|
901
|
+
// eslint-disable-next-line no-console -- error handling placeholder
|
|
902
|
+
console.error('Error in resize service message', message);
|
|
903
|
+
}
|
|
904
|
+
/**
|
|
905
|
+
* This method sets up a `ResizeObserver` to observe changes in the document's body height.
|
|
906
|
+
* When the height changes, it sends a resize message with the new height, to the connected peers
|
|
907
|
+
*/
|
|
908
|
+
startResizeObserver() {
|
|
909
|
+
this.resizeObserver = new ResizeObserver(() => {
|
|
910
|
+
const newHeight = document.body.getBoundingClientRect().height;
|
|
911
|
+
if (!this.actualHeight || newHeight !== this.actualHeight) {
|
|
912
|
+
this.actualHeight = newHeight;
|
|
913
|
+
const messageV10 = {
|
|
914
|
+
type: 'resize',
|
|
915
|
+
version: '1.0',
|
|
916
|
+
height: this.actualHeight
|
|
917
|
+
};
|
|
918
|
+
// TODO: sendBest() is not implemented -- https://github.com/AmadeusITGroup/microfrontends/issues/11
|
|
919
|
+
this.messageService.send(messageV10);
|
|
920
|
+
}
|
|
921
|
+
});
|
|
922
|
+
afterNextRender(() => this.resizeObserver?.observe(document.body));
|
|
923
|
+
}
|
|
924
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: ResizeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
925
|
+
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: ResizeService, providedIn: 'root' }); }
|
|
926
|
+
}
|
|
927
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: ResizeService, decorators: [{
|
|
928
|
+
type: Injectable,
|
|
929
|
+
args: [{
|
|
930
|
+
providedIn: 'root'
|
|
931
|
+
}]
|
|
932
|
+
}], ctorParameters: () => [] });
|
|
933
|
+
|
|
934
|
+
/**
|
|
935
|
+
* A directive that adjusts the height of an element based on resize messages from a specified channel.
|
|
936
|
+
*/
|
|
937
|
+
class ScalableDirective {
|
|
938
|
+
constructor() {
|
|
939
|
+
/**
|
|
940
|
+
* The connection ID for the element, used as channel id backup
|
|
941
|
+
*/
|
|
942
|
+
this.connect = input(...(ngDevMode ? [undefined, { debugName: "connect" }] : []));
|
|
943
|
+
/**
|
|
944
|
+
* The channel id
|
|
945
|
+
*/
|
|
946
|
+
this.scalable = input(...(ngDevMode ? [undefined, { debugName: "scalable" }] : []));
|
|
947
|
+
this.resizeHandler = inject(ResizeConsumerService);
|
|
948
|
+
/**
|
|
949
|
+
* This signal checks if the current channel requesting the resize matches the channel ID from the resize handler.
|
|
950
|
+
* If they match, it returns the new height information; otherwise, it returns undefined.
|
|
951
|
+
*/
|
|
952
|
+
this.newHeightFromChannel = computed(() => {
|
|
953
|
+
const channelAskingResize = this.scalable() || this.connect();
|
|
954
|
+
const newHeightFromChannel = this.resizeHandler.newHeightFromChannel();
|
|
955
|
+
if (channelAskingResize && newHeightFromChannel?.channelId === channelAskingResize) {
|
|
956
|
+
return newHeightFromChannel;
|
|
957
|
+
}
|
|
958
|
+
return undefined;
|
|
959
|
+
}, ...(ngDevMode ? [{ debugName: "newHeightFromChannel" }] : []));
|
|
960
|
+
const elem = inject(ElementRef);
|
|
961
|
+
const renderer = inject(Renderer2);
|
|
962
|
+
this.resizeHandler.start();
|
|
963
|
+
/** When a new height value is received set the height of the host element (in pixels) */
|
|
964
|
+
effect(() => {
|
|
965
|
+
const newHeightFromChannel = this.newHeightFromChannel();
|
|
966
|
+
if (newHeightFromChannel) {
|
|
967
|
+
renderer.setStyle(elem.nativeElement, 'height', `${newHeightFromChannel.height}px`);
|
|
968
|
+
}
|
|
969
|
+
});
|
|
970
|
+
}
|
|
971
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: ScalableDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
972
|
+
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.4", type: ScalableDirective, isStandalone: true, selector: "[scalable]", inputs: { connect: { classPropertyName: "connect", publicName: "connect", isSignal: true, isRequired: false, transformFunction: null }, scalable: { classPropertyName: "scalable", publicName: "scalable", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 }); }
|
|
973
|
+
}
|
|
974
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: ScalableDirective, decorators: [{
|
|
975
|
+
type: Directive,
|
|
976
|
+
args: [{
|
|
977
|
+
selector: '[scalable]',
|
|
978
|
+
standalone: true
|
|
979
|
+
}]
|
|
980
|
+
}], ctorParameters: () => [] });
|
|
981
|
+
|
|
799
982
|
/** Default suffix for an url containing a theme css file */
|
|
800
983
|
const THEME_URL_SUFFIX = '-theme.css';
|
|
801
984
|
/** Default name for the query parameter containing the theme name */
|
|
@@ -1049,122 +1232,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
|
|
|
1049
1232
|
}]
|
|
1050
1233
|
}], ctorParameters: () => [] });
|
|
1051
1234
|
|
|
1052
|
-
class ConnectDirective {
|
|
1053
|
-
/**
|
|
1054
|
-
* Binds the `src` attribute of the iframe to the sanitized source URL.
|
|
1055
|
-
*/
|
|
1056
|
-
get srcAttr() {
|
|
1057
|
-
return this.src();
|
|
1058
|
-
}
|
|
1059
|
-
constructor() {
|
|
1060
|
-
/**
|
|
1061
|
-
* The connection ID required for the message peer service.
|
|
1062
|
-
*/
|
|
1063
|
-
this.connect = input.required(...(ngDevMode ? [{ debugName: "connect" }] : []));
|
|
1064
|
-
/**
|
|
1065
|
-
* The sanitized source URL for the iframe.
|
|
1066
|
-
*/
|
|
1067
|
-
this.src = input(...(ngDevMode ? [undefined, { debugName: "src" }] : []));
|
|
1068
|
-
this.messageService = inject(MessagePeerService);
|
|
1069
|
-
this.domSanitizer = inject(DomSanitizer);
|
|
1070
|
-
this.iframeElement = inject(ElementRef).nativeElement;
|
|
1071
|
-
this.clientOrigin = computed(() => {
|
|
1072
|
-
const src = this.src();
|
|
1073
|
-
const srcString = src && this.domSanitizer.sanitize(SecurityContext.RESOURCE_URL, src);
|
|
1074
|
-
return srcString && new URL(srcString).origin;
|
|
1075
|
-
}, ...(ngDevMode ? [{ debugName: "clientOrigin" }] : []));
|
|
1076
|
-
const logger = inject(LoggerService);
|
|
1077
|
-
// When the origin or connection ID change - reconnect the message service
|
|
1078
|
-
effect((onCleanup) => {
|
|
1079
|
-
let stopHandshakeListening = () => { };
|
|
1080
|
-
const origin = this.clientOrigin();
|
|
1081
|
-
const id = this.connect();
|
|
1082
|
-
const source = this.iframeElement.contentWindow;
|
|
1083
|
-
// listen for handshakes only if we know the origin and were given a connection ID
|
|
1084
|
-
if (origin && source && id) {
|
|
1085
|
-
try {
|
|
1086
|
-
stopHandshakeListening = this.messageService.listen({ id, source, origin });
|
|
1087
|
-
}
|
|
1088
|
-
catch (e) {
|
|
1089
|
-
logger.error(`Failed to start listening for (connection ID: ${id})`, e);
|
|
1090
|
-
}
|
|
1091
|
-
}
|
|
1092
|
-
// stop listening for handshakes and disconnect previous connection when:
|
|
1093
|
-
// - origin/connection ID change
|
|
1094
|
-
// - the directive is destroyed
|
|
1095
|
-
onCleanup(() => {
|
|
1096
|
-
stopHandshakeListening();
|
|
1097
|
-
this.messageService.disconnect();
|
|
1098
|
-
});
|
|
1099
|
-
});
|
|
1100
|
-
}
|
|
1101
|
-
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: ConnectDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1102
|
-
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.4", type: ConnectDirective, isStandalone: true, selector: "iframe[connect]", inputs: { connect: { classPropertyName: "connect", publicName: "connect", isSignal: true, isRequired: true, transformFunction: null }, src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "src": "this.srcAttr" } }, ngImport: i0 }); }
|
|
1103
|
-
}
|
|
1104
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: ConnectDirective, decorators: [{
|
|
1105
|
-
type: Directive,
|
|
1106
|
-
args: [{
|
|
1107
|
-
selector: 'iframe[connect]',
|
|
1108
|
-
standalone: true
|
|
1109
|
-
}]
|
|
1110
|
-
}], ctorParameters: () => [], propDecorators: { srcAttr: [{
|
|
1111
|
-
type: HostBinding,
|
|
1112
|
-
args: ['src']
|
|
1113
|
-
}] } });
|
|
1114
|
-
|
|
1115
|
-
/**
|
|
1116
|
-
* No operation function used to override the history API methods.
|
|
1117
|
-
*/
|
|
1118
|
-
function noop() { }
|
|
1119
|
-
/**
|
|
1120
|
-
* If an iframe is not sandboxed or is of the same origin, navigation inside it will mess up the main window history.
|
|
1121
|
-
* This disables writing to and overriding history API from inside the iframe to prevent this.
|
|
1122
|
-
* Theoretically, this might break applications that rely on reading history API from inside the iframe.
|
|
1123
|
-
*
|
|
1124
|
-
* This should also allow having `CustomPathLocationStrategy` in the iframe if necessary.
|
|
1125
|
-
*/
|
|
1126
|
-
function provideDisableHistoryWrites() {
|
|
1127
|
-
return provideAppInitializer(() => {
|
|
1128
|
-
Object.defineProperty(history, 'pushState', { value: noop, writable: false, configurable: false });
|
|
1129
|
-
Object.defineProperty(history, 'replaceState', { value: noop, writable: false, configurable: false });
|
|
1130
|
-
});
|
|
1131
|
-
}
|
|
1132
|
-
|
|
1133
|
-
/**
|
|
1134
|
-
* Provide the communication protocol connection configuration
|
|
1135
|
-
* @param connectionConfigOptions The identifier of the application in the communication protocol ecosystem plus the types of messages able to exchange and a logger object
|
|
1136
|
-
*/
|
|
1137
|
-
function provideConnection(connectionConfigOptions) {
|
|
1138
|
-
persistHostInfo();
|
|
1139
|
-
const connectionId = (isEmbedded() && getHostInfo().moduleApplicationId) || connectionConfigOptions?.id;
|
|
1140
|
-
if (!connectionId) {
|
|
1141
|
-
(connectionConfigOptions?.logger || console).error('An id (moduleId) needs to be provided for the application in order to establish a connection inside the communication protocol');
|
|
1142
|
-
return makeEnvironmentProviders([]);
|
|
1143
|
-
}
|
|
1144
|
-
const config = {
|
|
1145
|
-
id: connectionId,
|
|
1146
|
-
messageCheckStrategy: 'version',
|
|
1147
|
-
knownMessages: [...KNOWN_MESSAGES, ...(connectionConfigOptions?.knownMessages || [])]
|
|
1148
|
-
};
|
|
1149
|
-
return makeEnvironmentProviders([
|
|
1150
|
-
{
|
|
1151
|
-
provide: MESSAGE_PEER_CONFIG, useValue: config
|
|
1152
|
-
},
|
|
1153
|
-
{
|
|
1154
|
-
provide: MESSAGE_PEER_CONNECT_OPTIONS, useValue: getDefaultClientEndpointStartOptions()
|
|
1155
|
-
},
|
|
1156
|
-
{
|
|
1157
|
-
// in the case of the ConnectionService will extend the base service 'useExisting' should be used
|
|
1158
|
-
provide: MessagePeerService, useClass: MessagePeerService, deps: [MESSAGE_PEER_CONFIG]
|
|
1159
|
-
},
|
|
1160
|
-
// deactivate history writes to avoid embedded app writing to the host history
|
|
1161
|
-
...isEmbedded() ? [provideDisableHistoryWrites()] : []
|
|
1162
|
-
]);
|
|
1163
|
-
}
|
|
1164
|
-
|
|
1165
1235
|
/**
|
|
1166
1236
|
* Generated bundle index. Do not edit.
|
|
1167
1237
|
*/
|
|
1168
1238
|
|
|
1169
|
-
export { ApplyTheme, ConnectDirective, ConsumerManagerService, ERROR_MESSAGE_TYPE, HostInfoPipe, KNOWN_MESSAGES, MFE_HOST_APPLICATION_ID_PARAM, MFE_HOST_URL_PARAM, MFE_MODULE_APPLICATION_ID_PARAM, NavigationConsumerService, ProducerManagerService, ResizeConsumerService, ResizeService, RestoreRoute, RouteMemorizeDirective, RouteMemorizeService, RoutingService, ScalableDirective, THEME_QUERY_PARAM_NAME, THEME_URL_SUFFIX, ThemeConsumerService, ThemeProducerService, applyInitialTheme, applyTheme, downloadApplicationThemeCss, getAvailableConsumers, getDefaultClientEndpointStartOptions, getHostInfo, getStyle, hostQueryParams, isEmbedded, isErrorMessage, persistHostInfo, provideConnection, registerConsumer, registerProducer, sendError };
|
|
1239
|
+
export { ApplyTheme, ConnectDirective, ConsumerManagerService, ERROR_MESSAGE_TYPE, HistoryConsumerService, HostInfoPipe, KNOWN_MESSAGES, MFE_HOST_APPLICATION_ID_PARAM, MFE_HOST_URL_PARAM, MFE_MODULE_APPLICATION_ID_PARAM, NavigationConsumerService, ProducerManagerService, ResizeConsumerService, ResizeService, RestoreRoute, RouteMemorizeDirective, RouteMemorizeService, RoutingService, ScalableDirective, THEME_QUERY_PARAM_NAME, THEME_URL_SUFFIX, ThemeConsumerService, ThemeProducerService, applyInitialTheme, applyTheme, downloadApplicationThemeCss, getAvailableConsumers, getDefaultClientEndpointStartOptions, getHostInfo, getStyle, hostQueryParams, isEmbedded, isErrorMessage, persistHostInfo, provideConnection, provideHistoryOverrides, registerConsumer, registerProducer, sendError };
|
|
1170
1240
|
//# sourceMappingURL=ama-mfe-ng-utils.mjs.map
|