@genesislcap/foundation-layout 14.395.0 → 14.396.1
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/POPOUT_TODO.md +46 -0
- package/dist/custom-elements.json +351 -24
- package/dist/dts/index.d.ts +1 -1
- package/dist/dts/index.d.ts.map +1 -1
- package/dist/dts/main/layout-main.d.ts +9 -4
- package/dist/dts/main/layout-main.d.ts.map +1 -1
- package/dist/dts/main/layout-popout-controller.d.ts +49 -0
- package/dist/dts/main/layout-popout-controller.d.ts.map +1 -0
- package/dist/dts/utils/constants.d.ts +7 -1
- package/dist/dts/utils/constants.d.ts.map +1 -1
- package/dist/dts/utils/index.d.ts +1 -0
- package/dist/dts/utils/index.d.ts.map +1 -1
- package/dist/dts/utils/popout-events.d.ts +43 -0
- package/dist/dts/utils/popout-events.d.ts.map +1 -0
- package/dist/dts/utils/types.d.ts +39 -0
- package/dist/dts/utils/types.d.ts.map +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/main/layout-main.js +48 -21
- package/dist/esm/main/layout-popout-controller.js +289 -0
- package/dist/esm/utils/constants.js +7 -1
- package/dist/esm/utils/index.js +1 -0
- package/dist/esm/utils/popout-events.js +5 -0
- package/dist/foundation-layout.api.json +72 -5
- package/dist/foundation-layout.d.ts +103 -3
- package/docs/api/foundation-layout.foundationlayout.md +2 -2
- package/docs/api/foundation-layout.foundationlayout.popoutconfig.md +2 -2
- package/docs/api/foundation-layout.layout_popout_control_key.md +16 -0
- package/docs/api/foundation-layout.layoutpopoutconfig.md +22 -0
- package/docs/api/foundation-layout.md +22 -0
- package/docs/api/foundation-layout.serialisedlayout.md +3 -0
- package/docs/api-report.md.api.md +28 -1
- package/package.json +15 -13
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { EventDetailMap } from '@genesislcap/foundation-events';
|
|
2
|
+
import { PopoutGeometry } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Events for layout popout communication
|
|
5
|
+
* @public
|
|
6
|
+
*/
|
|
7
|
+
export interface LayoutPopoutEvents extends EventDetailMap {
|
|
8
|
+
/**
|
|
9
|
+
* Emitted by a popout window when its geometry or component state changes
|
|
10
|
+
*/
|
|
11
|
+
'popout-update': {
|
|
12
|
+
layoutKey: string;
|
|
13
|
+
geometry: PopoutGeometry;
|
|
14
|
+
state?: unknown;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Emitted by a popout window when it is closed
|
|
18
|
+
*/
|
|
19
|
+
'popout-closed': {
|
|
20
|
+
layoutKey: string;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Emitted by a popout window when it opens to request state
|
|
24
|
+
*/
|
|
25
|
+
'popout-handshake': {
|
|
26
|
+
layoutKey: string;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Emitted by the host to sync state to a popout window
|
|
30
|
+
*/
|
|
31
|
+
'popout-sync': {
|
|
32
|
+
layoutKey: string;
|
|
33
|
+
geometry?: PopoutGeometry;
|
|
34
|
+
state?: unknown;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Name of the default broadcast channel used for layout popouts
|
|
39
|
+
* @public
|
|
40
|
+
*/
|
|
41
|
+
export declare const LAYOUT_POPOUT_CHANNEL_NAME = "f-layout-popout-channel";
|
|
42
|
+
export { PopoutGeometry };
|
|
43
|
+
//# sourceMappingURL=popout-events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"popout-events.d.ts","sourceRoot":"","sources":["../../../src/utils/popout-events.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAEzC;;;GAGG;AACH,MAAM,WAAW,kBAAmB,SAAQ,cAAc;IACxD;;OAEG;IACH,eAAe,EAAE;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,cAAc,CAAC;QACzB,KAAK,CAAC,EAAE,OAAO,CAAC;KACjB,CAAC;IACF;;OAEG;IACH,eAAe,EAAE;QACf,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IACF;;OAEG;IACH,kBAAkB,EAAE;QAClB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IACF;;OAEG;IACH,aAAa,EAAE;QACb,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,cAAc,CAAC;QAC1B,KAAK,CAAC,EAAE,OAAO,CAAC;KACjB,CAAC;CACH;AAED;;;GAGG;AACH,eAAO,MAAM,0BAA0B,4BAA4B,CAAC;AAEpE,OAAO,EAAE,cAAc,EAAE,CAAC"}
|
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
import { ComponentContainer, ResolvedLayoutConfig } from '@genesis-community/golden-layout';
|
|
2
2
|
import { componentType, instanceContainer } from './constants';
|
|
3
|
+
/**
|
|
4
|
+
* Geometry details for a popout window
|
|
5
|
+
* @public
|
|
6
|
+
*/
|
|
7
|
+
export type PopoutGeometry = {
|
|
8
|
+
screenX: number;
|
|
9
|
+
screenY: number;
|
|
10
|
+
outerWidth: number;
|
|
11
|
+
outerHeight: number;
|
|
12
|
+
screenId?: string;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* @beta
|
|
16
|
+
* State of a popped out window
|
|
17
|
+
*/
|
|
18
|
+
export type PopoutState = {
|
|
19
|
+
geometry: PopoutGeometry;
|
|
20
|
+
state?: unknown;
|
|
21
|
+
};
|
|
3
22
|
/**
|
|
4
23
|
* Definition of a custom button which will be added to all layout items.
|
|
5
24
|
* @remarks
|
|
@@ -25,6 +44,13 @@ export type CustomButton = {
|
|
|
25
44
|
export type SerialisedLayout = {
|
|
26
45
|
v: '1';
|
|
27
46
|
c: ResolvedLayoutConfig;
|
|
47
|
+
/**
|
|
48
|
+
* Optional registry of popped out components and their window geometry.
|
|
49
|
+
* @beta
|
|
50
|
+
*/
|
|
51
|
+
popouts?: {
|
|
52
|
+
[layoutKey: string]: PopoutState;
|
|
53
|
+
};
|
|
28
54
|
};
|
|
29
55
|
/**
|
|
30
56
|
* Interface to implement on an item which is a component of the layout and you wish to serialise state with. This is saved separately for each instance of the component, which allows you to restore multiple instances of the same component with different state.
|
|
@@ -200,4 +226,17 @@ export interface LayoutComponent {
|
|
|
200
226
|
export type AutosavedLayouts = {
|
|
201
227
|
[x: string]: string;
|
|
202
228
|
};
|
|
229
|
+
/** @beta */
|
|
230
|
+
export type LayoutPopoutConfig = {
|
|
231
|
+
/**
|
|
232
|
+
* The name of the broadcast channel to use for communication between the main window and popout windows.
|
|
233
|
+
* Defaults to 'f-layout-popout-channel'.
|
|
234
|
+
*/
|
|
235
|
+
channelName?: string;
|
|
236
|
+
loadAutomatically?: boolean;
|
|
237
|
+
popoutDimension?: {
|
|
238
|
+
width: number;
|
|
239
|
+
height: number;
|
|
240
|
+
};
|
|
241
|
+
};
|
|
203
242
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/utils/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAC;AAC5F,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAE/D;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;CAC9D,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,CAAC,EAAE,GAAG,CAAC;IACP,CAAC,EAAE,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/utils/types.ts"],"names":[],"mappings":"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"}
|
package/dist/esm/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export * from './main';
|
|
2
2
|
export { LAYOUT_ICONS } from './styles';
|
|
3
|
-
export { DEFAULT_RELOAD_BUFFER, LayoutEmitEvents, LayoutReceiveEvents, LayoutRegistrationError, LayoutUsageError, LAYOUT_POPOUT_CONTAINER_CLASS, } from './utils';
|
|
3
|
+
export { DEFAULT_RELOAD_BUFFER, LayoutEmitEvents, LayoutReceiveEvents, LayoutRegistrationError, LayoutUsageError, LAYOUT_POPOUT_CONTAINER_CLASS, LAYOUT_POPOUT_CONTROL_KEY, } from './utils';
|
|
4
4
|
export { registerFactory, getFactory, unregisterFactory } from './utils/factory-registry';
|
|
@@ -11,7 +11,9 @@ import { AUTOSAVE_KEY, componentType, DEFAULT_RELOAD_BUFFER, getMissingArrayItem
|
|
|
11
11
|
import { LayoutRegistrationError, LayoutUsageError } from '../utils/error';
|
|
12
12
|
import { getFactory } from '../utils/factory-registry';
|
|
13
13
|
import { logger } from '../utils/logger';
|
|
14
|
+
import { FoundationLayoutPopoutController } from './layout-popout-controller';
|
|
14
15
|
export { layoutStyles } from '../styles';
|
|
16
|
+
FoundationLayoutPopoutController;
|
|
15
17
|
/*
|
|
16
18
|
* We only want to apply the global stylesheet required for golden layout
|
|
17
19
|
* to work once, and only apply it if we actually use an instance of the
|
|
@@ -101,13 +103,14 @@ export class FoundationLayout extends FoundationElement {
|
|
|
101
103
|
* has changed and know you potentially need to gate some of your lifecycle functionality.
|
|
102
104
|
*/
|
|
103
105
|
this.lifecycleUpdateToken = undefined;
|
|
106
|
+
/** @internal */
|
|
107
|
+
this.popupMode = false;
|
|
104
108
|
/**
|
|
105
109
|
* Controls whether popout functionality is enabled on the layout. Defaults to disabled.
|
|
106
|
-
*
|
|
110
|
+
* Pass an empty object `{}` to use default configurations, or a {@link LayoutPopoutConfig} object to further customise.
|
|
107
111
|
* @beta
|
|
108
112
|
*/
|
|
109
113
|
this.popoutConfig = undefined;
|
|
110
|
-
this.popupMode = false;
|
|
111
114
|
/** @internal */
|
|
112
115
|
_FoundationLayout__boundDragListener.set(this, undefined);
|
|
113
116
|
/** @internal */
|
|
@@ -256,6 +259,7 @@ export class FoundationLayout extends FoundationElement {
|
|
|
256
259
|
* @returns - latest version of {@link SerialisedLayout} describing the layout
|
|
257
260
|
*/
|
|
258
261
|
getLayout() {
|
|
262
|
+
var _b, _c;
|
|
259
263
|
const componentCollection = this.getLayoutComponents();
|
|
260
264
|
componentCollection.forEach((items) => {
|
|
261
265
|
if (!items.length)
|
|
@@ -276,7 +280,11 @@ export class FoundationLayout extends FoundationElement {
|
|
|
276
280
|
orderedStates,
|
|
277
281
|
});
|
|
278
282
|
});
|
|
279
|
-
return {
|
|
283
|
+
return {
|
|
284
|
+
v: '1',
|
|
285
|
+
c: ResolvedLayoutConfig.minifyConfig(this.layout.saveLayout()),
|
|
286
|
+
popouts: (_c = (_b = this.popoutController) === null || _b === void 0 ? void 0 : _b.getRegistry()) !== null && _c !== void 0 ? _c : {},
|
|
287
|
+
};
|
|
280
288
|
}
|
|
281
289
|
/**
|
|
282
290
|
* If in a popout window from the dynamic layout, this function will run the flow to put the component in popout mode.
|
|
@@ -287,13 +295,14 @@ export class FoundationLayout extends FoundationElement {
|
|
|
287
295
|
* If you set the `LAYOUT_POPOUT_CONTAINER_CLASS` on an element which is a DOM parent of the layout,
|
|
288
296
|
* then if the layout goes into popout mode then it will place itself as the only child for the popout container you set.
|
|
289
297
|
* It is likely you'll want to attach this class to your design system provider.
|
|
298
|
+
*
|
|
290
299
|
* @beta
|
|
291
300
|
*/
|
|
292
301
|
tryActivatePopoutMode() {
|
|
293
302
|
if (!this.popupMode)
|
|
294
303
|
return false;
|
|
295
304
|
const popoutComponentRegistration = new URLSearchParams(window.location.search).get(LAYOUT_POPOUT_CONTROL_KEY);
|
|
296
|
-
const
|
|
305
|
+
const glPopoutConfig = {
|
|
297
306
|
root: {
|
|
298
307
|
type: 'component',
|
|
299
308
|
componentType: popoutComponentRegistration,
|
|
@@ -303,7 +312,7 @@ export class FoundationLayout extends FoundationElement {
|
|
|
303
312
|
hasHeaders: false,
|
|
304
313
|
},
|
|
305
314
|
};
|
|
306
|
-
this.loadGLConfigAndSetup(
|
|
315
|
+
this.loadGLConfigAndSetup(glPopoutConfig);
|
|
307
316
|
const tryFindNewLayoutDOMLocation = (e) => {
|
|
308
317
|
if (e.classList.contains(LAYOUT_POPOUT_CONTAINER_CLASS))
|
|
309
318
|
return e;
|
|
@@ -397,6 +406,7 @@ export class FoundationLayout extends FoundationElement {
|
|
|
397
406
|
* @throws various errors if the layout string is malformed and cannot be parsed
|
|
398
407
|
*/
|
|
399
408
|
loadLayout(layout, handleMissingItem = 'error', disableCache = false) {
|
|
409
|
+
var _b;
|
|
400
410
|
const alreadyRegistered = this.registeredItems();
|
|
401
411
|
const wantedRegistered = FoundationLayout.layoutRequiredRegistrations(layout);
|
|
402
412
|
const missingRegisteredItems = getMissingArrayItems(wantedRegistered, alreadyRegistered);
|
|
@@ -408,6 +418,9 @@ export class FoundationLayout extends FoundationElement {
|
|
|
408
418
|
const layoutConfig = LayoutConfig.fromResolved(ResolvedLayoutConfig.unminifyConfig(layout.c));
|
|
409
419
|
if (disableCache)
|
|
410
420
|
this.removeConfigCacheInformation(layoutConfig);
|
|
421
|
+
if (!this.popupMode && this.popoutController) {
|
|
422
|
+
this.popoutController.restorePopouts((_b = layout.popouts) !== null && _b !== void 0 ? _b : {}, this.uuid);
|
|
423
|
+
}
|
|
411
424
|
if (missingRegisteredItems.length !== 0 && handleMissingItem === 'placeholder') {
|
|
412
425
|
this.registerPlaceholdersAndSetClosable(layoutConfig, missingRegisteredItems);
|
|
413
426
|
}
|
|
@@ -639,12 +652,14 @@ export class FoundationLayout extends FoundationElement {
|
|
|
639
652
|
return;
|
|
640
653
|
this.reloadPending = true;
|
|
641
654
|
setTimeout(() => {
|
|
655
|
+
var _b;
|
|
642
656
|
this.reloadPending = false;
|
|
643
657
|
if (!this.hasFirstLoaded) {
|
|
644
658
|
this.hasFirstLoaded = true;
|
|
645
659
|
// Store the default layout config before attempting to load from storage
|
|
646
660
|
this.defaultLayoutConfig = JSON.parse(JSON.stringify(this.layoutConfig));
|
|
647
|
-
const res = this.tryLoadLayoutFromLocalStorage() ||
|
|
661
|
+
const res = this.tryLoadLayoutFromLocalStorage() ||
|
|
662
|
+
(((_b = this.popoutConfig) === null || _b === void 0 ? void 0 : _b.loadAutomatically) !== false && this.tryActivatePopoutMode());
|
|
648
663
|
if (!res) {
|
|
649
664
|
this.loadGLConfigAndSetup(this.layoutConfig);
|
|
650
665
|
}
|
|
@@ -973,23 +988,25 @@ export class FoundationLayout extends FoundationElement {
|
|
|
973
988
|
if (!this.popoutConfig)
|
|
974
989
|
return;
|
|
975
990
|
logger.warn('Layout pop-out mode is enabled, this is an experimental feature and may change in future versions');
|
|
976
|
-
const
|
|
977
|
-
const matcher = /(\d+);(\d+)/;
|
|
978
|
-
const mMatches = popoutConfig.match(matcher);
|
|
979
|
-
if (!mMatches)
|
|
980
|
-
return '';
|
|
981
|
-
const [, width, height] = mMatches;
|
|
982
|
-
return `,width=${width},height=${height}`;
|
|
983
|
-
};
|
|
991
|
+
const configWithDefaults = Object.assign(Object.assign({}, this.popoutConfig), { loadAutomatically: true, popoutDimension: Object.assign(Object.assign({}, this.popoutConfig.popoutDimension), { width: 960, height: 720 }) });
|
|
984
992
|
this.customButtons.push({
|
|
985
993
|
svg: LAYOUT_ICONS.popoutSVG,
|
|
986
994
|
onClick: (_, elem) => {
|
|
987
|
-
|
|
988
|
-
const
|
|
989
|
-
const
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
995
|
+
var _b, _c;
|
|
996
|
+
const instance = elem.firstChild[instanceContainer];
|
|
997
|
+
const registration = instance.registration;
|
|
998
|
+
const component = elem.firstChild;
|
|
999
|
+
const state = (_b = component === null || component === void 0 ? void 0 : component.getCurrentState) === null || _b === void 0 ? void 0 : _b.call(component);
|
|
1000
|
+
// Use default dimensions from config
|
|
1001
|
+
const defaultGeometry = {
|
|
1002
|
+
screenX: undefined,
|
|
1003
|
+
screenY: undefined,
|
|
1004
|
+
outerWidth: configWithDefaults.popoutDimension.width,
|
|
1005
|
+
outerHeight: configWithDefaults.popoutDimension.height,
|
|
1006
|
+
};
|
|
1007
|
+
// Add to controller registry immediately so it tracks it
|
|
1008
|
+
(_c = this.popoutController) === null || _c === void 0 ? void 0 : _c.openPopout(registration, { geometry: defaultGeometry, state }, this.uuid);
|
|
1009
|
+
this.cacheAndSaveLayout();
|
|
993
1010
|
},
|
|
994
1011
|
});
|
|
995
1012
|
this.popupMode = window.location.search.includes(LAYOUT_POPOUT_CONTROL_KEY);
|
|
@@ -1021,7 +1038,10 @@ __decorate([
|
|
|
1021
1038
|
observable
|
|
1022
1039
|
], FoundationLayout.prototype, "dragging", void 0);
|
|
1023
1040
|
__decorate([
|
|
1024
|
-
|
|
1041
|
+
observable
|
|
1042
|
+
], FoundationLayout.prototype, "popupMode", void 0);
|
|
1043
|
+
__decorate([
|
|
1044
|
+
observable
|
|
1025
1045
|
], FoundationLayout.prototype, "popoutConfig", void 0);
|
|
1026
1046
|
__decorate([
|
|
1027
1047
|
observable
|
|
@@ -1037,6 +1057,13 @@ const loadingTemplate = html `
|
|
|
1037
1057
|
*/
|
|
1038
1058
|
export const layoutTemplate = html `
|
|
1039
1059
|
<template>
|
|
1060
|
+
${when((x) => Boolean(x.popoutConfig), html `
|
|
1061
|
+
<foundation-layout-popout-controller
|
|
1062
|
+
${ref('popoutController')}
|
|
1063
|
+
:parentLayout="${(x) => x}"
|
|
1064
|
+
channel-name="${(x) => { var _b; return (_b = x.popoutConfig) === null || _b === void 0 ? void 0 : _b.channelName; }}"
|
|
1065
|
+
></foundation-layout-popout-controller>
|
|
1066
|
+
`)}
|
|
1040
1067
|
${when((x) => !x.hasFirstLoaded && x.usingDeclerativeAPI, loadingTemplate)}
|
|
1041
1068
|
<div class="layout-container" ${ref('layoutElement')}></div>
|
|
1042
1069
|
</template>
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import { __awaiter, __decorate } from "tslib";
|
|
2
|
+
import { createTypedBroadcastChannel, } from '@genesislcap/foundation-broadcast-channel';
|
|
3
|
+
import { attr, customElement, FASTElement, observable } from '@microsoft/fast-element';
|
|
4
|
+
import { LAYOUT_POPOUT_CONTROL_KEY, LayoutReceiveEvents, POPOUT_GEOMETRY_BROADCAST_INTERVAL, } from '../utils';
|
|
5
|
+
import { LAYOUT_POPOUT_CHANNEL_NAME } from '../utils/popout-events';
|
|
6
|
+
let FoundationLayoutPopoutController = class FoundationLayoutPopoutController extends FASTElement {
|
|
7
|
+
constructor() {
|
|
8
|
+
super(...arguments);
|
|
9
|
+
// Internal registry to track both geometry and active window references
|
|
10
|
+
this.popoutRegistry = new Map();
|
|
11
|
+
}
|
|
12
|
+
connectedCallback() {
|
|
13
|
+
super.connectedCallback();
|
|
14
|
+
this.layoutKey = new URLSearchParams(window.location.search).get(LAYOUT_POPOUT_CONTROL_KEY);
|
|
15
|
+
this.setupChannel();
|
|
16
|
+
this.setupUnloadListener();
|
|
17
|
+
if (this.layoutKey) {
|
|
18
|
+
this.startPolling();
|
|
19
|
+
this.channel.postMessage('popout-handshake', { layoutKey: this.layoutKey });
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
this.setupListener();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
disconnectedCallback() {
|
|
26
|
+
super.disconnectedCallback();
|
|
27
|
+
if (this.channel) {
|
|
28
|
+
this.channel.close();
|
|
29
|
+
}
|
|
30
|
+
this.stopPolling();
|
|
31
|
+
this.teardownUnloadListener();
|
|
32
|
+
if (!this.layoutKey) {
|
|
33
|
+
this.closeAllPopouts();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
setupChannel() {
|
|
37
|
+
const name = this.channelName || LAYOUT_POPOUT_CHANNEL_NAME;
|
|
38
|
+
this.channel = createTypedBroadcastChannel(name);
|
|
39
|
+
if (this.layoutKey) {
|
|
40
|
+
this.channel.onmessage = (ev) => {
|
|
41
|
+
if (this.channel.isMessageType('popout-sync', ev)) {
|
|
42
|
+
const { layoutKey, state } = ev.data.detail;
|
|
43
|
+
if (layoutKey === this.layoutKey && state !== undefined) {
|
|
44
|
+
this.applyStateToParent(state);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
setupListener() {
|
|
51
|
+
if (!this.channel)
|
|
52
|
+
return;
|
|
53
|
+
this.channel.onmessage = (ev) => {
|
|
54
|
+
if (this.channel.isMessageType('popout-update', ev)) {
|
|
55
|
+
const { layoutKey, geometry, state } = ev.data
|
|
56
|
+
.detail;
|
|
57
|
+
const current = this.popoutRegistry.get(layoutKey);
|
|
58
|
+
this.popoutRegistry.set(layoutKey, Object.assign(Object.assign({}, current), { geometry, state }));
|
|
59
|
+
this.emitAutosave();
|
|
60
|
+
}
|
|
61
|
+
else if (this.channel.isMessageType('popout-closed', ev)) {
|
|
62
|
+
const { layoutKey } = ev.data.detail;
|
|
63
|
+
this.popoutRegistry.delete(layoutKey);
|
|
64
|
+
this.emitAutosave();
|
|
65
|
+
}
|
|
66
|
+
else if (this.channel.isMessageType('popout-handshake', ev)) {
|
|
67
|
+
const { layoutKey } = ev.data.detail;
|
|
68
|
+
const current = this.popoutRegistry.get(layoutKey);
|
|
69
|
+
if (current) {
|
|
70
|
+
this.channel.postMessage('popout-sync', {
|
|
71
|
+
layoutKey,
|
|
72
|
+
geometry: current.geometry,
|
|
73
|
+
state: current.state,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
applyStateToParent(state) {
|
|
80
|
+
if (!this.parentLayout)
|
|
81
|
+
return;
|
|
82
|
+
const components = this.parentLayout.getLayoutComponents();
|
|
83
|
+
for (const group of components) {
|
|
84
|
+
for (const item of group) {
|
|
85
|
+
if (item && typeof item.applyState === 'function') {
|
|
86
|
+
item.applyState(state);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
setupUnloadListener() {
|
|
92
|
+
this.unloadHandler = () => {
|
|
93
|
+
if (this.layoutKey) {
|
|
94
|
+
if (this.channel) {
|
|
95
|
+
this.channel.postMessage('popout-closed', { layoutKey: this.layoutKey });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
this.closeAllPopouts();
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
window.addEventListener('beforeunload', this.unloadHandler);
|
|
103
|
+
}
|
|
104
|
+
teardownUnloadListener() {
|
|
105
|
+
if (this.unloadHandler) {
|
|
106
|
+
window.removeEventListener('beforeunload', this.unloadHandler);
|
|
107
|
+
this.unloadHandler = null;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
emitAutosave() {
|
|
111
|
+
this.$emit(LayoutReceiveEvents.autosave, undefined, {
|
|
112
|
+
bubbles: true,
|
|
113
|
+
composed: true,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
startPolling() {
|
|
117
|
+
this.pollInterval = setInterval(() => __awaiter(this, void 0, void 0, function* () {
|
|
118
|
+
const geometry = yield this.getGeometry();
|
|
119
|
+
const state = this.getComponentState();
|
|
120
|
+
const update = { geometry, state };
|
|
121
|
+
const updateString = JSON.stringify(update);
|
|
122
|
+
if (updateString !== this.lastUpdate) {
|
|
123
|
+
this.broadcastUpdate(geometry, state);
|
|
124
|
+
this.lastUpdate = updateString;
|
|
125
|
+
}
|
|
126
|
+
}), POPOUT_GEOMETRY_BROADCAST_INTERVAL);
|
|
127
|
+
}
|
|
128
|
+
stopPolling() {
|
|
129
|
+
if (this.pollInterval) {
|
|
130
|
+
clearInterval(this.pollInterval);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
getGeometry() {
|
|
134
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
135
|
+
const screenId = yield (() => __awaiter(this, void 0, void 0, function* () {
|
|
136
|
+
var _a;
|
|
137
|
+
if ('getScreenDetails' in window) {
|
|
138
|
+
try {
|
|
139
|
+
const details = yield window.getScreenDetails();
|
|
140
|
+
return (_a = details.currentScreen) === null || _a === void 0 ? void 0 : _a.label;
|
|
141
|
+
}
|
|
142
|
+
catch (e) {
|
|
143
|
+
return undefined;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return undefined;
|
|
147
|
+
}))();
|
|
148
|
+
return {
|
|
149
|
+
screenX: window.screenX,
|
|
150
|
+
screenY: window.screenY,
|
|
151
|
+
outerWidth: window.outerWidth,
|
|
152
|
+
outerHeight: window.outerHeight,
|
|
153
|
+
screenId,
|
|
154
|
+
};
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
getComponentState() {
|
|
158
|
+
if (!this.parentLayout)
|
|
159
|
+
return undefined;
|
|
160
|
+
const components = this.parentLayout.getLayoutComponents();
|
|
161
|
+
// In a popout window, we expect exactly one component
|
|
162
|
+
for (const group of components) {
|
|
163
|
+
for (const item of group) {
|
|
164
|
+
if (item && typeof item.getCurrentState === 'function') {
|
|
165
|
+
return item.getCurrentState();
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return undefined;
|
|
170
|
+
}
|
|
171
|
+
broadcastUpdate(geometry, state) {
|
|
172
|
+
if (!this.channel || !this.layoutKey)
|
|
173
|
+
return;
|
|
174
|
+
this.channel.postMessage('popout-update', {
|
|
175
|
+
layoutKey: this.layoutKey,
|
|
176
|
+
geometry,
|
|
177
|
+
state,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Opens a component in a popout window.
|
|
182
|
+
* @param registration - The registration ID of the component to pop out.
|
|
183
|
+
* @param popoutState - Optional state (geometry and component state) to use for the new window.
|
|
184
|
+
* @param uuid - UUID generator from parent layout
|
|
185
|
+
*/
|
|
186
|
+
openPopout(registration, popoutState, uuid) {
|
|
187
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
188
|
+
const url = new URL(window.location.toString());
|
|
189
|
+
const itemParams = new URLSearchParams();
|
|
190
|
+
itemParams.append(LAYOUT_POPOUT_CONTROL_KEY, registration);
|
|
191
|
+
url.search = itemParams.toString();
|
|
192
|
+
let features = 'popup';
|
|
193
|
+
const geometry = popoutState === null || popoutState === void 0 ? void 0 : popoutState.geometry;
|
|
194
|
+
let targetLeft = geometry === null || geometry === void 0 ? void 0 : geometry.screenX;
|
|
195
|
+
let targetTop = geometry === null || geometry === void 0 ? void 0 : geometry.screenY;
|
|
196
|
+
if ((geometry === null || geometry === void 0 ? void 0 : geometry.screenId) && 'getScreenDetails' in window) {
|
|
197
|
+
try {
|
|
198
|
+
const details = yield window.getScreenDetails();
|
|
199
|
+
const targetScreen = details.screens.find((s) => s.label === geometry.screenId);
|
|
200
|
+
if (targetScreen) {
|
|
201
|
+
const isWithin = targetLeft >= targetScreen.availLeft &&
|
|
202
|
+
targetLeft < targetScreen.availLeft + targetScreen.availWidth &&
|
|
203
|
+
targetTop >= targetScreen.availTop &&
|
|
204
|
+
targetTop < targetScreen.availTop + targetScreen.availHeight;
|
|
205
|
+
if (!isWithin) {
|
|
206
|
+
targetLeft = targetScreen.availLeft;
|
|
207
|
+
targetTop = targetScreen.availTop;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
targetLeft = undefined;
|
|
212
|
+
targetTop = undefined;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
catch (e) {
|
|
216
|
+
// Fallback
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
if (geometry) {
|
|
220
|
+
features += `,width=${geometry.outerWidth},height=${geometry.outerHeight}`;
|
|
221
|
+
if (targetLeft !== undefined)
|
|
222
|
+
features += `,left=${targetLeft}`;
|
|
223
|
+
if (targetTop !== undefined)
|
|
224
|
+
features += `,top=${targetTop}`;
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
features += ',width=800,height=600';
|
|
228
|
+
}
|
|
229
|
+
const win = window.open(url, `popout-${registration}-${(uuid === null || uuid === void 0 ? void 0 : uuid.createId()) || Date.now()}`, features);
|
|
230
|
+
if (!win) {
|
|
231
|
+
console.warn(`Popout window for ${registration} was blocked.`);
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
this.popoutRegistry.set(registration, {
|
|
235
|
+
geometry: geometry || {},
|
|
236
|
+
state: popoutState === null || popoutState === void 0 ? void 0 : popoutState.state,
|
|
237
|
+
window: win,
|
|
238
|
+
});
|
|
239
|
+
return win;
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
getRegistry() {
|
|
243
|
+
const output = {};
|
|
244
|
+
for (const [key, val] of this.popoutRegistry.entries()) {
|
|
245
|
+
output[key] = { geometry: val.geometry, state: val.state };
|
|
246
|
+
}
|
|
247
|
+
return output;
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Restores popouts from a registry
|
|
251
|
+
*/
|
|
252
|
+
restorePopouts(registry, uuid) {
|
|
253
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
254
|
+
this.closeAllPopouts();
|
|
255
|
+
for (const [key, val] of Object.entries(registry)) {
|
|
256
|
+
this.popoutRegistry.set(key, { geometry: val.geometry, state: val.state });
|
|
257
|
+
}
|
|
258
|
+
const keys = Array.from(this.popoutRegistry.keys());
|
|
259
|
+
if (keys.length === 0)
|
|
260
|
+
return;
|
|
261
|
+
yield Promise.all(keys.map((key) => {
|
|
262
|
+
const state = this.popoutRegistry.get(key);
|
|
263
|
+
return this.openPopout(key, state, uuid);
|
|
264
|
+
}));
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
closeAllPopouts() {
|
|
268
|
+
for (const val of this.popoutRegistry.values()) {
|
|
269
|
+
if (val.window && !val.window.closed) {
|
|
270
|
+
val.window.close();
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
this.popoutRegistry.clear();
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
__decorate([
|
|
277
|
+
attr({ attribute: 'channel-name' })
|
|
278
|
+
], FoundationLayoutPopoutController.prototype, "channelName", void 0);
|
|
279
|
+
__decorate([
|
|
280
|
+
observable
|
|
281
|
+
], FoundationLayoutPopoutController.prototype, "parentLayout", void 0);
|
|
282
|
+
FoundationLayoutPopoutController = __decorate([
|
|
283
|
+
customElement({
|
|
284
|
+
name: 'foundation-layout-popout-controller',
|
|
285
|
+
template: null,
|
|
286
|
+
styles: null,
|
|
287
|
+
})
|
|
288
|
+
], FoundationLayoutPopoutController);
|
|
289
|
+
export { FoundationLayoutPopoutController };
|
|
@@ -25,9 +25,15 @@ export const DEFAULT_RELOAD_BUFFER = 500;
|
|
|
25
25
|
export const AUTOSAVE_KEY = 'foundation-layout-autosave';
|
|
26
26
|
/**
|
|
27
27
|
* Key to be used for controlling popout behaviour
|
|
28
|
-
* @
|
|
28
|
+
* @beta
|
|
29
29
|
*/
|
|
30
30
|
export const LAYOUT_POPOUT_CONTROL_KEY = 'f-layout-key';
|
|
31
|
+
/**
|
|
32
|
+
* How often a popout window should check to broadcast its
|
|
33
|
+
* geometry
|
|
34
|
+
* @interal
|
|
35
|
+
*/
|
|
36
|
+
export const POPOUT_GEOMETRY_BROADCAST_INTERVAL = 1000;
|
|
31
37
|
/**
|
|
32
38
|
* Put this classname on an element which is a DOM parent of the layout, and
|
|
33
39
|
* if the layout goes into popout mode then it will place itself as the only child
|
package/dist/esm/utils/index.js
CHANGED