@genesis-community/golden-layout 2.6.0
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/LICENSE +21 -0
- package/README.md +24 -0
- package/dist/cjs/index.js +40 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/ts/config/config.js +870 -0
- package/dist/cjs/ts/config/config.js.map +1 -0
- package/dist/cjs/ts/config/resolved-config.js +477 -0
- package/dist/cjs/ts/config/resolved-config.js.map +1 -0
- package/dist/cjs/ts/container/component-container.js +412 -0
- package/dist/cjs/ts/container/component-container.js.map +1 -0
- package/dist/cjs/ts/controls/browser-popout.js +298 -0
- package/dist/cjs/ts/controls/browser-popout.js.map +1 -0
- package/dist/cjs/ts/controls/drag-proxy.js +221 -0
- package/dist/cjs/ts/controls/drag-proxy.js.map +1 -0
- package/dist/cjs/ts/controls/drag-source.js +149 -0
- package/dist/cjs/ts/controls/drag-source.js.map +1 -0
- package/dist/cjs/ts/controls/drop-target-indicator.js +31 -0
- package/dist/cjs/ts/controls/drop-target-indicator.js.map +1 -0
- package/dist/cjs/ts/controls/header-button.js +34 -0
- package/dist/cjs/ts/controls/header-button.js.map +1 -0
- package/dist/cjs/ts/controls/header.js +366 -0
- package/dist/cjs/ts/controls/header.js.map +1 -0
- package/dist/cjs/ts/controls/splitter.js +42 -0
- package/dist/cjs/ts/controls/splitter.js.map +1 -0
- package/dist/cjs/ts/controls/tab.js +262 -0
- package/dist/cjs/ts/controls/tab.js.map +1 -0
- package/dist/cjs/ts/controls/tabs-container.js +236 -0
- package/dist/cjs/ts/controls/tabs-container.js.map +1 -0
- package/dist/cjs/ts/controls/transition-indicator.js +64 -0
- package/dist/cjs/ts/controls/transition-indicator.js.map +1 -0
- package/dist/cjs/ts/errors/external-error.js +46 -0
- package/dist/cjs/ts/errors/external-error.js.map +1 -0
- package/dist/cjs/ts/errors/internal-error.js +38 -0
- package/dist/cjs/ts/errors/internal-error.js.map +1 -0
- package/dist/cjs/ts/golden-layout.js +299 -0
- package/dist/cjs/ts/golden-layout.js.map +1 -0
- package/dist/cjs/ts/items/component-item.js +190 -0
- package/dist/cjs/ts/items/component-item.js.map +1 -0
- package/dist/cjs/ts/items/component-parentable-item.js +18 -0
- package/dist/cjs/ts/items/component-parentable-item.js.map +1 -0
- package/dist/cjs/ts/items/content-item.js +414 -0
- package/dist/cjs/ts/items/content-item.js.map +1 -0
- package/dist/cjs/ts/items/ground-item.js +352 -0
- package/dist/cjs/ts/items/ground-item.js.map +1 -0
- package/dist/cjs/ts/items/row-or-column.js +609 -0
- package/dist/cjs/ts/items/row-or-column.js.map +1 -0
- package/dist/cjs/ts/items/stack.js +841 -0
- package/dist/cjs/ts/items/stack.js.map +1 -0
- package/dist/cjs/ts/layout-manager.js +1618 -0
- package/dist/cjs/ts/layout-manager.js.map +1 -0
- package/dist/cjs/ts/utils/config-minifier.js +218 -0
- package/dist/cjs/ts/utils/config-minifier.js.map +1 -0
- package/dist/cjs/ts/utils/dom-constants.js +3 -0
- package/dist/cjs/ts/utils/dom-constants.js.map +1 -0
- package/dist/cjs/ts/utils/drag-listener.js +132 -0
- package/dist/cjs/ts/utils/drag-listener.js.map +1 -0
- package/dist/cjs/ts/utils/event-emitter.js +201 -0
- package/dist/cjs/ts/utils/event-emitter.js.map +1 -0
- package/dist/cjs/ts/utils/event-hub.js +135 -0
- package/dist/cjs/ts/utils/event-hub.js.map +1 -0
- package/dist/cjs/ts/utils/i18n-strings.js +74 -0
- package/dist/cjs/ts/utils/i18n-strings.js.map +1 -0
- package/dist/cjs/ts/utils/jquery-legacy.js +15 -0
- package/dist/cjs/ts/utils/jquery-legacy.js.map +1 -0
- package/dist/cjs/ts/utils/style-constants.js +11 -0
- package/dist/cjs/ts/utils/style-constants.js.map +1 -0
- package/dist/cjs/ts/utils/types.js +94 -0
- package/dist/cjs/ts/utils/types.js.map +1 -0
- package/dist/cjs/ts/utils/utils.js +211 -0
- package/dist/cjs/ts/utils/utils.js.map +1 -0
- package/dist/cjs/ts/virtual-layout.js +247 -0
- package/dist/cjs/ts/virtual-layout.js.map +1 -0
- package/dist/css/goldenlayout-base.css +319 -0
- package/dist/css/themes/goldenlayout-borderless-dark-theme.css +136 -0
- package/dist/css/themes/goldenlayout-dark-theme.css +139 -0
- package/dist/css/themes/goldenlayout-light-theme.css +129 -0
- package/dist/css/themes/goldenlayout-soda-theme.css +126 -0
- package/dist/css/themes/goldenlayout-translucent-theme.css +152 -0
- package/dist/esm/index.js +21 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/ts/config/config.js +864 -0
- package/dist/esm/ts/config/config.js.map +1 -0
- package/dist/esm/ts/config/resolved-config.js +474 -0
- package/dist/esm/ts/config/resolved-config.js.map +1 -0
- package/dist/esm/ts/container/component-container.js +408 -0
- package/dist/esm/ts/container/component-container.js.map +1 -0
- package/dist/esm/ts/controls/browser-popout.js +294 -0
- package/dist/esm/ts/controls/browser-popout.js.map +1 -0
- package/dist/esm/ts/controls/drag-proxy.js +217 -0
- package/dist/esm/ts/controls/drag-proxy.js.map +1 -0
- package/dist/esm/ts/controls/drag-source.js +145 -0
- package/dist/esm/ts/controls/drag-source.js.map +1 -0
- package/dist/esm/ts/controls/drop-target-indicator.js +27 -0
- package/dist/esm/ts/controls/drop-target-indicator.js.map +1 -0
- package/dist/esm/ts/controls/header-button.js +30 -0
- package/dist/esm/ts/controls/header-button.js.map +1 -0
- package/dist/esm/ts/controls/header.js +362 -0
- package/dist/esm/ts/controls/header.js.map +1 -0
- package/dist/esm/ts/controls/splitter.js +38 -0
- package/dist/esm/ts/controls/splitter.js.map +1 -0
- package/dist/esm/ts/controls/tab.js +258 -0
- package/dist/esm/ts/controls/tab.js.map +1 -0
- package/dist/esm/ts/controls/tabs-container.js +232 -0
- package/dist/esm/ts/controls/tabs-container.js.map +1 -0
- package/dist/esm/ts/controls/transition-indicator.js +60 -0
- package/dist/esm/ts/controls/transition-indicator.js.map +1 -0
- package/dist/esm/ts/errors/external-error.js +38 -0
- package/dist/esm/ts/errors/external-error.js.map +1 -0
- package/dist/esm/ts/errors/internal-error.js +31 -0
- package/dist/esm/ts/errors/internal-error.js.map +1 -0
- package/dist/esm/ts/golden-layout.js +295 -0
- package/dist/esm/ts/golden-layout.js.map +1 -0
- package/dist/esm/ts/items/component-item.js +186 -0
- package/dist/esm/ts/items/component-item.js.map +1 -0
- package/dist/esm/ts/items/component-parentable-item.js +14 -0
- package/dist/esm/ts/items/component-parentable-item.js.map +1 -0
- package/dist/esm/ts/items/content-item.js +410 -0
- package/dist/esm/ts/items/content-item.js.map +1 -0
- package/dist/esm/ts/items/ground-item.js +348 -0
- package/dist/esm/ts/items/ground-item.js.map +1 -0
- package/dist/esm/ts/items/row-or-column.js +605 -0
- package/dist/esm/ts/items/row-or-column.js.map +1 -0
- package/dist/esm/ts/items/stack.js +837 -0
- package/dist/esm/ts/items/stack.js.map +1 -0
- package/dist/esm/ts/layout-manager.js +1614 -0
- package/dist/esm/ts/layout-manager.js.map +1 -0
- package/dist/esm/ts/utils/config-minifier.js +215 -0
- package/dist/esm/ts/utils/config-minifier.js.map +1 -0
- package/dist/esm/ts/utils/dom-constants.js +2 -0
- package/dist/esm/ts/utils/dom-constants.js.map +1 -0
- package/dist/esm/ts/utils/drag-listener.js +128 -0
- package/dist/esm/ts/utils/drag-listener.js.map +1 -0
- package/dist/esm/ts/utils/event-emitter.js +197 -0
- package/dist/esm/ts/utils/event-emitter.js.map +1 -0
- package/dist/esm/ts/utils/event-hub.js +131 -0
- package/dist/esm/ts/utils/event-hub.js.map +1 -0
- package/dist/esm/ts/utils/i18n-strings.js +71 -0
- package/dist/esm/ts/utils/i18n-strings.js.map +1 -0
- package/dist/esm/ts/utils/jquery-legacy.js +11 -0
- package/dist/esm/ts/utils/jquery-legacy.js.map +1 -0
- package/dist/esm/ts/utils/style-constants.js +8 -0
- package/dist/esm/ts/utils/style-constants.js.map +1 -0
- package/dist/esm/ts/utils/types.js +91 -0
- package/dist/esm/ts/utils/types.js.map +1 -0
- package/dist/esm/ts/utils/utils.js +191 -0
- package/dist/esm/ts/utils/utils.js.map +1 -0
- package/dist/esm/ts/virtual-layout.js +243 -0
- package/dist/esm/ts/virtual-layout.js.map +1 -0
- package/dist/img/lm_close_black.png +0 -0
- package/dist/img/lm_close_tab_white.png +0 -0
- package/dist/img/lm_close_white.png +0 -0
- package/dist/img/lm_maximise_black.png +0 -0
- package/dist/img/lm_maximise_white.png +0 -0
- package/dist/img/lm_minimize_black.png +0 -0
- package/dist/img/lm_minimize_white.png +0 -0
- package/dist/img/lm_popin_black.png +0 -0
- package/dist/img/lm_popin_white.png +0 -0
- package/dist/img/lm_popout_black.png +0 -0
- package/dist/img/lm_popout_white.png +0 -0
- package/dist/less/goldenlayout-base.less +422 -0
- package/dist/less/themes/goldenlayout-borderless-dark-theme.less +230 -0
- package/dist/less/themes/goldenlayout-dark-theme.less +233 -0
- package/dist/less/themes/goldenlayout-light-theme.less +223 -0
- package/dist/less/themes/goldenlayout-soda-theme.less +211 -0
- package/dist/less/themes/goldenlayout-translucent-theme.less +237 -0
- package/dist/scss/goldenlayout-base.scss +422 -0
- package/dist/scss/themes/_goldenlayout-var-theme.scss +232 -0
- package/dist/types/golden-layout-untrimmed.d.ts +3428 -0
- package/dist/types/index.d.ts +2246 -0
- package/dist/types/tsdoc-metadata.json +11 -0
- package/package.json +107 -0
- package/src/TOOLCHAIN.md +54 -0
- package/src/img/lm_close_black.png +0 -0
- package/src/img/lm_close_tab_white.png +0 -0
- package/src/img/lm_close_white.png +0 -0
- package/src/img/lm_maximise_black.png +0 -0
- package/src/img/lm_maximise_white.png +0 -0
- package/src/img/lm_minimize_black.png +0 -0
- package/src/img/lm_minimize_white.png +0 -0
- package/src/img/lm_popin_black.png +0 -0
- package/src/img/lm_popin_white.png +0 -0
- package/src/img/lm_popout_black.png +0 -0
- package/src/img/lm_popout_white.png +0 -0
- package/src/index.ts +21 -0
- package/src/less/goldenlayout-base.less +422 -0
- package/src/less/themes/goldenlayout-borderless-dark-theme.less +230 -0
- package/src/less/themes/goldenlayout-dark-theme.less +233 -0
- package/src/less/themes/goldenlayout-light-theme.less +223 -0
- package/src/less/themes/goldenlayout-soda-theme.less +211 -0
- package/src/less/themes/goldenlayout-translucent-theme.less +237 -0
- package/src/scss/goldenlayout-base.scss +422 -0
- package/src/scss/themes/_goldenlayout-var-theme.scss +232 -0
- package/src/ts/config/config.ts +1283 -0
- package/src/ts/config/resolved-config.ts +621 -0
- package/src/ts/container/component-container.ts +500 -0
- package/src/ts/controls/browser-popout.ts +325 -0
- package/src/ts/controls/drag-proxy.ts +259 -0
- package/src/ts/controls/drag-source.ts +167 -0
- package/src/ts/controls/drop-target-indicator.ts +35 -0
- package/src/ts/controls/header-button.ts +39 -0
- package/src/ts/controls/header.ts +483 -0
- package/src/ts/controls/splitter.ts +50 -0
- package/src/ts/controls/tab.ts +293 -0
- package/src/ts/controls/tabs-container.ts +281 -0
- package/src/ts/controls/transition-indicator.ts +78 -0
- package/src/ts/errors/external-error.ts +39 -0
- package/src/ts/errors/internal-error.ts +34 -0
- package/src/ts/golden-layout.ts +365 -0
- package/src/ts/items/component-item.ts +252 -0
- package/src/ts/items/component-parentable-item.ts +16 -0
- package/src/ts/items/content-item.ts +513 -0
- package/src/ts/items/ground-item.ts +404 -0
- package/src/ts/items/row-or-column.ts +707 -0
- package/src/ts/items/stack.ts +975 -0
- package/src/ts/layout-manager.ts +1862 -0
- package/src/ts/utils/config-minifier.ts +235 -0
- package/src/ts/utils/dom-constants.ts +44 -0
- package/src/ts/utils/drag-listener.ts +178 -0
- package/src/ts/utils/event-emitter.ts +275 -0
- package/src/ts/utils/event-hub.ts +163 -0
- package/src/ts/utils/i18n-strings.ts +96 -0
- package/src/ts/utils/jquery-legacy.ts +12 -0
- package/src/ts/utils/style-constants.ts +6 -0
- package/src/ts/utils/types.ts +145 -0
- package/src/ts/utils/utils.ts +206 -0
- package/src/ts/virtual-layout.ts +328 -0
|
@@ -0,0 +1,1862 @@
|
|
|
1
|
+
import { ComponentItemConfig, ItemConfig, LayoutConfig, RowOrColumnItemConfig, StackItemConfig } from './config/config';
|
|
2
|
+
import {
|
|
3
|
+
ResolvedComponentItemConfig,
|
|
4
|
+
ResolvedItemConfig,
|
|
5
|
+
ResolvedLayoutConfig,
|
|
6
|
+
ResolvedPopoutLayoutConfig,
|
|
7
|
+
ResolvedRootItemConfig,
|
|
8
|
+
ResolvedRowOrColumnItemConfig,
|
|
9
|
+
ResolvedStackItemConfig
|
|
10
|
+
} from "./config/resolved-config";
|
|
11
|
+
import { ComponentContainer } from './container/component-container';
|
|
12
|
+
import { BrowserPopout } from './controls/browser-popout';
|
|
13
|
+
import { DragProxy } from './controls/drag-proxy';
|
|
14
|
+
import { DragSource } from './controls/drag-source';
|
|
15
|
+
import { DropTargetIndicator } from './controls/drop-target-indicator';
|
|
16
|
+
import { TransitionIndicator } from './controls/transition-indicator';
|
|
17
|
+
import { ConfigurationError } from './errors/external-error';
|
|
18
|
+
import { AssertError, UnexpectedNullError, UnexpectedUndefinedError, UnreachableCaseError } from './errors/internal-error';
|
|
19
|
+
import { ComponentItem } from './items/component-item';
|
|
20
|
+
import { ComponentParentableItem } from './items/component-parentable-item';
|
|
21
|
+
import { ContentItem } from './items/content-item';
|
|
22
|
+
import { GroundItem } from './items/ground-item';
|
|
23
|
+
import { RowOrColumn } from './items/row-or-column';
|
|
24
|
+
import { Stack } from './items/stack';
|
|
25
|
+
import { ConfigMinifier } from './utils/config-minifier';
|
|
26
|
+
import { DomConstants } from './utils/dom-constants';
|
|
27
|
+
import { DragListener } from './utils/drag-listener';
|
|
28
|
+
import { EventEmitter } from './utils/event-emitter';
|
|
29
|
+
import { EventHub } from './utils/event-hub';
|
|
30
|
+
import { I18nStringId, I18nStrings, i18nStrings } from './utils/i18n-strings';
|
|
31
|
+
import { ItemType, JsonValue, Rect, ResponsiveMode } from './utils/types';
|
|
32
|
+
import {
|
|
33
|
+
getElementWidthAndHeight,
|
|
34
|
+
removeFromArray,
|
|
35
|
+
setElementHeight,
|
|
36
|
+
setElementWidth
|
|
37
|
+
} from './utils/utils';
|
|
38
|
+
|
|
39
|
+
/** @internal */
|
|
40
|
+
declare global {
|
|
41
|
+
interface Window {
|
|
42
|
+
__glInstance: LayoutManager;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The main class that will be exposed as GoldenLayout.
|
|
48
|
+
*/
|
|
49
|
+
|
|
50
|
+
/** @public */
|
|
51
|
+
export abstract class LayoutManager extends EventEmitter {
|
|
52
|
+
/** Whether the layout will be automatically be resized to container whenever the container's size is changed
|
|
53
|
+
* Default is true if <body> is the container otherwise false
|
|
54
|
+
* Default will be changed to true for any container in the future
|
|
55
|
+
*/
|
|
56
|
+
resizeWithContainerAutomatically = false;
|
|
57
|
+
/** The debounce interval (in milliseconds) used whenever a layout is automatically resized. 0 means next tick */
|
|
58
|
+
resizeDebounceInterval = 100;
|
|
59
|
+
/** Extend the current debounce delay time period if it is triggered during the delay.
|
|
60
|
+
* If this is true, the layout will only resize when its container has stopped being resized.
|
|
61
|
+
* If it is false, the layout will resize at intervals while its container is being resized.
|
|
62
|
+
*/
|
|
63
|
+
resizeDebounceExtendedWhenPossible = true;
|
|
64
|
+
|
|
65
|
+
/** @internal */
|
|
66
|
+
private _containerElement: HTMLElement;
|
|
67
|
+
/** @internal */
|
|
68
|
+
private _isInitialised = false;
|
|
69
|
+
/** @internal */
|
|
70
|
+
private _groundItem: GroundItem | undefined = undefined;
|
|
71
|
+
/** @internal */
|
|
72
|
+
private _openPopouts: BrowserPopout[] = [];
|
|
73
|
+
/** @internal */
|
|
74
|
+
private _dropTargetIndicator: DropTargetIndicator | null = null;
|
|
75
|
+
/** @internal */
|
|
76
|
+
private _transitionIndicator: TransitionIndicator | null = null;
|
|
77
|
+
/** @internal */
|
|
78
|
+
private _resizeTimeoutId: ReturnType<typeof setTimeout> | undefined;
|
|
79
|
+
/** @internal */
|
|
80
|
+
private _itemAreas: ContentItem.Area[] = [];
|
|
81
|
+
/** @internal */
|
|
82
|
+
private _maximisedStack: Stack | undefined;
|
|
83
|
+
/** @internal */
|
|
84
|
+
private _maximisePlaceholder = LayoutManager.createMaximisePlaceElement(document);
|
|
85
|
+
/** @internal */
|
|
86
|
+
private _tabDropPlaceholder = LayoutManager.createTabDropPlaceholderElement(document);
|
|
87
|
+
/** @internal */
|
|
88
|
+
private _dragSources: DragSource[] = [];
|
|
89
|
+
/** @internal */
|
|
90
|
+
private _updatingColumnsResponsive = false;
|
|
91
|
+
/** @internal */
|
|
92
|
+
private _firstLoad = true;
|
|
93
|
+
/** @internal */
|
|
94
|
+
private _eventHub = new EventHub(this);
|
|
95
|
+
/** @internal */
|
|
96
|
+
private _width: number | null = null;
|
|
97
|
+
/** @internal */
|
|
98
|
+
private _height: number | null = null;
|
|
99
|
+
/** @internal */
|
|
100
|
+
private _focusedComponentItem: ComponentItem | undefined;
|
|
101
|
+
/** @internal */
|
|
102
|
+
private _virtualSizedContainers: ComponentContainer[] = [];
|
|
103
|
+
/** @internal */
|
|
104
|
+
private _virtualSizedContainerAddingBeginCount = 0;
|
|
105
|
+
/** @internal */
|
|
106
|
+
private _sizeInvalidationBeginCount = 0;
|
|
107
|
+
/** @internal */
|
|
108
|
+
protected _constructorOrSubWindowLayoutConfig: LayoutConfig | undefined; // protected for backwards compatibility
|
|
109
|
+
|
|
110
|
+
/** @internal */
|
|
111
|
+
private _resizeObserver = new ResizeObserver(() => this.handleContainerResize());
|
|
112
|
+
/** @internal @deprecated to be removed in version 3 */
|
|
113
|
+
private _windowBeforeUnloadListener = () => this.onBeforeUnload();
|
|
114
|
+
/** @internal @deprecated to be removed in version 3 */
|
|
115
|
+
private _windowBeforeUnloadListening = false;
|
|
116
|
+
/** @internal */
|
|
117
|
+
private _maximisedStackBeforeDestroyedListener = (ev: EventEmitter.BubblingEvent) => this.cleanupBeforeMaximisedStackDestroyed(ev);
|
|
118
|
+
|
|
119
|
+
readonly isSubWindow: boolean;
|
|
120
|
+
layoutConfig: ResolvedLayoutConfig;
|
|
121
|
+
|
|
122
|
+
beforeVirtualRectingEvent: LayoutManager.BeforeVirtualRectingEvent | undefined;
|
|
123
|
+
afterVirtualRectingEvent: LayoutManager.AfterVirtualRectingEvent | undefined;
|
|
124
|
+
|
|
125
|
+
get container(): HTMLElement { return this._containerElement; }
|
|
126
|
+
get isInitialised(): boolean { return this._isInitialised; }
|
|
127
|
+
/** @internal */
|
|
128
|
+
get groundItem(): GroundItem | undefined { return this._groundItem; }
|
|
129
|
+
/** @internal @deprecated use {@link (LayoutManager:class).groundItem} instead */
|
|
130
|
+
get root(): GroundItem | undefined { return this._groundItem; }
|
|
131
|
+
get openPopouts(): BrowserPopout[] { return this._openPopouts; }
|
|
132
|
+
/** @internal */
|
|
133
|
+
get dropTargetIndicator(): DropTargetIndicator | null { return this._dropTargetIndicator; }
|
|
134
|
+
/** @internal @deprecated To be removed */
|
|
135
|
+
get transitionIndicator(): TransitionIndicator | null { return this._transitionIndicator; }
|
|
136
|
+
get width(): number | null { return this._width; }
|
|
137
|
+
get height(): number | null { return this._height; }
|
|
138
|
+
/**
|
|
139
|
+
* Retrieves the {@link (EventHub:class)} instance associated with this layout manager.
|
|
140
|
+
* This can be used to propagate events between the windows
|
|
141
|
+
* @public
|
|
142
|
+
*/
|
|
143
|
+
get eventHub(): EventHub { return this._eventHub; }
|
|
144
|
+
get rootItem(): ContentItem | undefined {
|
|
145
|
+
if (this._groundItem === undefined) {
|
|
146
|
+
throw new Error('Cannot access rootItem before init');
|
|
147
|
+
} else {
|
|
148
|
+
const groundContentItems = this._groundItem.contentItems;
|
|
149
|
+
if (groundContentItems.length === 0) {
|
|
150
|
+
return undefined;
|
|
151
|
+
} else {
|
|
152
|
+
return this._groundItem.contentItems[0];
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
get focusedComponentItem(): ComponentItem | undefined { return this._focusedComponentItem; }
|
|
157
|
+
/** @internal */
|
|
158
|
+
get tabDropPlaceholder(): HTMLElement { return this._tabDropPlaceholder; }
|
|
159
|
+
get maximisedStack(): Stack | undefined { return this._maximisedStack; }
|
|
160
|
+
|
|
161
|
+
/** @deprecated indicates deprecated constructor use */
|
|
162
|
+
get deprecatedConstructor(): boolean { return !this.isSubWindow && this._constructorOrSubWindowLayoutConfig !== undefined; }
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* @param container - A Dom HTML element. Defaults to body
|
|
167
|
+
* @internal
|
|
168
|
+
*/
|
|
169
|
+
constructor(parameters: LayoutManager.ConstructorParameters) {
|
|
170
|
+
super();
|
|
171
|
+
|
|
172
|
+
this.isSubWindow = parameters.isSubWindow;
|
|
173
|
+
|
|
174
|
+
this._constructorOrSubWindowLayoutConfig = parameters.constructorOrSubWindowLayoutConfig;
|
|
175
|
+
|
|
176
|
+
I18nStrings.checkInitialise();
|
|
177
|
+
ConfigMinifier.checkInitialise();
|
|
178
|
+
|
|
179
|
+
if (parameters.containerElement !== undefined) {
|
|
180
|
+
this._containerElement = parameters.containerElement;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Destroys the LayoutManager instance itself as well as every ContentItem
|
|
186
|
+
* within it. After this is called nothing should be left of the LayoutManager.
|
|
187
|
+
*
|
|
188
|
+
* This function only needs to be called if an application wishes to destroy the Golden Layout object while
|
|
189
|
+
* a page remains loaded. When a page is unloaded, all resources claimed by Golden Layout will automatically
|
|
190
|
+
* be released.
|
|
191
|
+
*/
|
|
192
|
+
destroy(): void {
|
|
193
|
+
if (this._isInitialised) {
|
|
194
|
+
if (this._windowBeforeUnloadListening) {
|
|
195
|
+
globalThis.removeEventListener('beforeunload', this._windowBeforeUnloadListener);
|
|
196
|
+
this._windowBeforeUnloadListening = false;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (this.layoutConfig.settings.closePopoutsOnUnload === true) {
|
|
200
|
+
this.closeAllOpenPopouts();
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
this._resizeObserver.disconnect();
|
|
204
|
+
this.checkClearResizeTimeout();
|
|
205
|
+
|
|
206
|
+
if (this._groundItem !== undefined) {
|
|
207
|
+
this._groundItem.destroy();
|
|
208
|
+
}
|
|
209
|
+
this._tabDropPlaceholder.remove();
|
|
210
|
+
if (this._dropTargetIndicator !== null) {
|
|
211
|
+
this._dropTargetIndicator.destroy();
|
|
212
|
+
}
|
|
213
|
+
if (this._transitionIndicator !== null) {
|
|
214
|
+
this._transitionIndicator.destroy();
|
|
215
|
+
}
|
|
216
|
+
this._eventHub.destroy();
|
|
217
|
+
|
|
218
|
+
for (const dragSource of this._dragSources) {
|
|
219
|
+
dragSource.destroy();
|
|
220
|
+
}
|
|
221
|
+
this._dragSources = [];
|
|
222
|
+
|
|
223
|
+
this._isInitialised = false;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Takes a GoldenLayout configuration object and
|
|
229
|
+
* replaces its keys and values recursively with
|
|
230
|
+
* one letter codes
|
|
231
|
+
* @deprecated use {@link (ResolvedLayoutConfig:namespace).minifyConfig} instead
|
|
232
|
+
*/
|
|
233
|
+
minifyConfig(config: ResolvedLayoutConfig): ResolvedLayoutConfig {
|
|
234
|
+
return ResolvedLayoutConfig.minifyConfig(config);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Takes a configuration Object that was previously minified
|
|
239
|
+
* using minifyConfig and returns its original version
|
|
240
|
+
* @deprecated use {@link (ResolvedLayoutConfig:namespace).unminifyConfig} instead
|
|
241
|
+
*/
|
|
242
|
+
unminifyConfig(config: ResolvedLayoutConfig): ResolvedLayoutConfig {
|
|
243
|
+
return ResolvedLayoutConfig.unminifyConfig(config);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/** @internal */
|
|
247
|
+
abstract bindComponent(container: ComponentContainer, itemConfig: ResolvedComponentItemConfig): ComponentContainer.BindableComponent;
|
|
248
|
+
/** @internal */
|
|
249
|
+
abstract unbindComponent(container: ComponentContainer, virtual: boolean, component: ComponentContainer.Component | undefined): void;
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Called from GoldenLayout class. Finishes of init
|
|
253
|
+
* @internal
|
|
254
|
+
*/
|
|
255
|
+
init(): void {
|
|
256
|
+
this.setContainer();
|
|
257
|
+
this._dropTargetIndicator = new DropTargetIndicator(/*this.container*/);
|
|
258
|
+
this._transitionIndicator = new TransitionIndicator();
|
|
259
|
+
this.updateSizeFromContainer();
|
|
260
|
+
|
|
261
|
+
let subWindowRootConfig: ComponentItemConfig | undefined;
|
|
262
|
+
if (this.isSubWindow) {
|
|
263
|
+
if (this._constructorOrSubWindowLayoutConfig === undefined) {
|
|
264
|
+
// SubWindow LayoutConfig should have been generated by constructor
|
|
265
|
+
throw new UnexpectedUndefinedError('LMIU07155');
|
|
266
|
+
} else {
|
|
267
|
+
const root = this._constructorOrSubWindowLayoutConfig.root;
|
|
268
|
+
if (root === undefined) {
|
|
269
|
+
// SubWindow LayoutConfig must not be empty
|
|
270
|
+
throw new AssertError('LMIC07156');
|
|
271
|
+
} else {
|
|
272
|
+
if (ItemConfig.isComponent(root)) {
|
|
273
|
+
subWindowRootConfig = root;
|
|
274
|
+
} else {
|
|
275
|
+
// SubWindow LayoutConfig must have Component as Root
|
|
276
|
+
throw new AssertError('LMIC07157');
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
const resolvedLayoutConfig = LayoutConfig.resolve(this._constructorOrSubWindowLayoutConfig);
|
|
280
|
+
// remove root from layoutConfig
|
|
281
|
+
this.layoutConfig = {
|
|
282
|
+
...resolvedLayoutConfig,
|
|
283
|
+
root: undefined,
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
} else {
|
|
287
|
+
if (this._constructorOrSubWindowLayoutConfig === undefined) {
|
|
288
|
+
this.layoutConfig = ResolvedLayoutConfig.createDefault(); // will overwritten be loaded via loadLayout
|
|
289
|
+
} else {
|
|
290
|
+
// backwards compatibility
|
|
291
|
+
this.layoutConfig = LayoutConfig.resolve(this._constructorOrSubWindowLayoutConfig);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
const layoutConfig = this.layoutConfig;
|
|
295
|
+
this._groundItem = new GroundItem(this, layoutConfig.root, this._containerElement);
|
|
296
|
+
this._groundItem.init();
|
|
297
|
+
|
|
298
|
+
this.checkLoadedLayoutMaximiseItem();
|
|
299
|
+
|
|
300
|
+
this._resizeObserver.observe(this._containerElement);
|
|
301
|
+
this._isInitialised = true;
|
|
302
|
+
this.adjustColumnsResponsive();
|
|
303
|
+
this.emit('initialised');
|
|
304
|
+
|
|
305
|
+
if (subWindowRootConfig !== undefined) {
|
|
306
|
+
// must be SubWindow
|
|
307
|
+
this.loadComponentAsRoot(subWindowRootConfig);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Loads a new layout
|
|
313
|
+
* @param layoutConfig - New layout to be loaded
|
|
314
|
+
*/
|
|
315
|
+
loadLayout(layoutConfig: LayoutConfig): void {
|
|
316
|
+
if (!this.isInitialised) {
|
|
317
|
+
// In case application not correctly using legacy constructor
|
|
318
|
+
throw new Error('GoldenLayout: Need to call init() if LayoutConfig with defined root passed to constructor')
|
|
319
|
+
} else {
|
|
320
|
+
if (this._groundItem === undefined) {
|
|
321
|
+
throw new UnexpectedUndefinedError('LMLL11119');
|
|
322
|
+
} else {
|
|
323
|
+
this.createSubWindows(); // still needs to be tested
|
|
324
|
+
|
|
325
|
+
this.layoutConfig = LayoutConfig.resolve(layoutConfig);
|
|
326
|
+
this._groundItem.loadRoot(this.layoutConfig.root);
|
|
327
|
+
this.checkLoadedLayoutMaximiseItem();
|
|
328
|
+
this.adjustColumnsResponsive();
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Creates a layout configuration object based on the the current state
|
|
335
|
+
*
|
|
336
|
+
* @public
|
|
337
|
+
* @returns GoldenLayout configuration
|
|
338
|
+
*/
|
|
339
|
+
saveLayout(): ResolvedLayoutConfig {
|
|
340
|
+
if (this._isInitialised === false) {
|
|
341
|
+
throw new Error('Can\'t create config, layout not yet initialised');
|
|
342
|
+
} else {
|
|
343
|
+
|
|
344
|
+
// if (root !== undefined && !(root instanceof ContentItem)) {
|
|
345
|
+
// throw new Error('Root must be a ContentItem');
|
|
346
|
+
// }
|
|
347
|
+
|
|
348
|
+
/*
|
|
349
|
+
* Content
|
|
350
|
+
*/
|
|
351
|
+
if (this._groundItem === undefined) {
|
|
352
|
+
throw new UnexpectedUndefinedError('LMTC18244');
|
|
353
|
+
} else {
|
|
354
|
+
const groundContent = this._groundItem.calculateConfigContent();
|
|
355
|
+
|
|
356
|
+
let rootItemConfig: ResolvedRootItemConfig | undefined;
|
|
357
|
+
if (groundContent.length !== 1) {
|
|
358
|
+
rootItemConfig = undefined;
|
|
359
|
+
} else {
|
|
360
|
+
rootItemConfig = groundContent[0];
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/*
|
|
364
|
+
* Retrieve config for subwindows
|
|
365
|
+
*/
|
|
366
|
+
this.reconcilePopoutWindows();
|
|
367
|
+
const openPopouts: ResolvedPopoutLayoutConfig[] = [];
|
|
368
|
+
for (let i = 0; i < this._openPopouts.length; i++) {
|
|
369
|
+
openPopouts.push(this._openPopouts[i].toConfig());
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const config: ResolvedLayoutConfig = {
|
|
373
|
+
root: rootItemConfig,
|
|
374
|
+
openPopouts,
|
|
375
|
+
settings: ResolvedLayoutConfig.Settings.createCopy(this.layoutConfig.settings),
|
|
376
|
+
dimensions: ResolvedLayoutConfig.Dimensions.createCopy(this.layoutConfig.dimensions),
|
|
377
|
+
header: ResolvedLayoutConfig.Header.createCopy(this.layoutConfig.header),
|
|
378
|
+
resolved: true,
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return config;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Removes any existing layout. Effectively, an empty layout will be loaded.
|
|
388
|
+
*/
|
|
389
|
+
|
|
390
|
+
clear(): void {
|
|
391
|
+
if (this._groundItem === undefined) {
|
|
392
|
+
throw new UnexpectedUndefinedError('LMCL11129');
|
|
393
|
+
} else {
|
|
394
|
+
this._groundItem.clearRoot();
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* @deprecated Use {@link (LayoutManager:class).saveLayout}
|
|
400
|
+
*/
|
|
401
|
+
toConfig(): ResolvedLayoutConfig {
|
|
402
|
+
return this.saveLayout();
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Adds a new ComponentItem. Will use default location selectors to ensure a location is found and
|
|
407
|
+
* component is successfully added
|
|
408
|
+
* @param componentTypeName - Name of component type to be created.
|
|
409
|
+
* @param state - Optional initial state to be assigned to component
|
|
410
|
+
* @returns New ComponentItem created.
|
|
411
|
+
*/
|
|
412
|
+
newComponent(componentType: JsonValue, componentState?: JsonValue, title?: string): ComponentItem {
|
|
413
|
+
const componentItem = this.newComponentAtLocation(componentType, componentState, title);
|
|
414
|
+
if (componentItem === undefined) {
|
|
415
|
+
throw new AssertError('LMNC65588');
|
|
416
|
+
} else {
|
|
417
|
+
return componentItem;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Adds a ComponentItem at the first valid selector location.
|
|
423
|
+
* @param componentTypeName - Name of component type to be created.
|
|
424
|
+
* @param state - Optional initial state to be assigned to component
|
|
425
|
+
* @param locationSelectors - Array of location selectors used to find location in layout where component
|
|
426
|
+
* will be added. First location in array which is valid will be used. If locationSelectors is undefined,
|
|
427
|
+
* {@link (LayoutManager:namespace).defaultLocationSelectors} will be used
|
|
428
|
+
* @returns New ComponentItem created or undefined if no valid location selector was in array.
|
|
429
|
+
*/
|
|
430
|
+
newComponentAtLocation(componentType: JsonValue, componentState?: JsonValue, title?: string,
|
|
431
|
+
locationSelectors?: LayoutManager.LocationSelector[]
|
|
432
|
+
): ComponentItem | undefined{
|
|
433
|
+
if (this._groundItem === undefined) {
|
|
434
|
+
throw new Error('Cannot add component before init');
|
|
435
|
+
} else {
|
|
436
|
+
const location = this.addComponentAtLocation(componentType, componentState, title, locationSelectors);
|
|
437
|
+
if (location === undefined) {
|
|
438
|
+
return undefined;
|
|
439
|
+
} else {
|
|
440
|
+
const createdItem = location.parentItem.contentItems[location.index];
|
|
441
|
+
if (!ContentItem.isComponentItem(createdItem)) {
|
|
442
|
+
throw new AssertError('LMNC992877533');
|
|
443
|
+
} else {
|
|
444
|
+
return createdItem;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Adds a new ComponentItem. Will use default location selectors to ensure a location is found and
|
|
452
|
+
* component is successfully added
|
|
453
|
+
* @param componentType - Type of component to be created.
|
|
454
|
+
* @param state - Optional initial state to be assigned to component
|
|
455
|
+
* @returns Location of new ComponentItem created.
|
|
456
|
+
*/
|
|
457
|
+
addComponent(componentType: JsonValue, componentState?: JsonValue, title?: string): LayoutManager.Location {
|
|
458
|
+
const location = this.addComponentAtLocation(componentType, componentState, title);
|
|
459
|
+
if (location === undefined) {
|
|
460
|
+
throw new AssertError('LMAC99943');
|
|
461
|
+
} else {
|
|
462
|
+
return location;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* Adds a ComponentItem at the first valid selector location.
|
|
468
|
+
* @param componentType - Type of component to be created.
|
|
469
|
+
* @param state - Optional initial state to be assigned to component
|
|
470
|
+
* @param locationSelectors - Array of location selectors used to find determine location in layout where component
|
|
471
|
+
* will be added. First location in array which is valid will be used. If undefined,
|
|
472
|
+
* {@link (LayoutManager:namespace).defaultLocationSelectors} will be used.
|
|
473
|
+
* @returns Location of new ComponentItem created or undefined if no valid location selector was in array.
|
|
474
|
+
*/
|
|
475
|
+
addComponentAtLocation(componentType: JsonValue, componentState?: JsonValue, title?: string,
|
|
476
|
+
locationSelectors?: readonly LayoutManager.LocationSelector[]
|
|
477
|
+
): LayoutManager.Location | undefined {
|
|
478
|
+
const itemConfig: ComponentItemConfig = {
|
|
479
|
+
type: 'component',
|
|
480
|
+
componentType,
|
|
481
|
+
componentState,
|
|
482
|
+
title,
|
|
483
|
+
};
|
|
484
|
+
|
|
485
|
+
return this.addItemAtLocation(itemConfig, locationSelectors);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* Adds a new ContentItem. Will use default location selectors to ensure a location is found and
|
|
490
|
+
* component is successfully added
|
|
491
|
+
* @param itemConfig - ResolvedItemConfig of child to be added.
|
|
492
|
+
* @returns New ContentItem created.
|
|
493
|
+
*/
|
|
494
|
+
newItem(itemConfig: RowOrColumnItemConfig | StackItemConfig | ComponentItemConfig): ContentItem {
|
|
495
|
+
const contentItem = this.newItemAtLocation(itemConfig);
|
|
496
|
+
if (contentItem === undefined) {
|
|
497
|
+
throw new AssertError('LMNC65588');
|
|
498
|
+
} else {
|
|
499
|
+
return contentItem;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/**
|
|
504
|
+
* Adds a new child ContentItem under the root ContentItem. If a root does not exist, then create root ContentItem instead
|
|
505
|
+
* @param itemConfig - ResolvedItemConfig of child to be added.
|
|
506
|
+
* @param locationSelectors - Array of location selectors used to find determine location in layout where ContentItem
|
|
507
|
+
* will be added. First location in array which is valid will be used. If undefined,
|
|
508
|
+
* {@link (LayoutManager:namespace).defaultLocationSelectors} will be used.
|
|
509
|
+
* @returns New ContentItem created or undefined if no valid location selector was in array. */
|
|
510
|
+
newItemAtLocation(itemConfig: RowOrColumnItemConfig | StackItemConfig | ComponentItemConfig,
|
|
511
|
+
locationSelectors?: readonly LayoutManager.LocationSelector[]
|
|
512
|
+
): ContentItem | undefined {
|
|
513
|
+
if (this._groundItem === undefined) {
|
|
514
|
+
throw new Error('Cannot add component before init');
|
|
515
|
+
} else {
|
|
516
|
+
const location = this.addItemAtLocation(itemConfig, locationSelectors);
|
|
517
|
+
if (location === undefined) {
|
|
518
|
+
return undefined;
|
|
519
|
+
} else {
|
|
520
|
+
const createdItem = location.parentItem.contentItems[location.index];
|
|
521
|
+
return createdItem;
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* Adds a new ContentItem. Will use default location selectors to ensure a location is found and
|
|
528
|
+
* component is successfully added.
|
|
529
|
+
* @param itemConfig - ResolvedItemConfig of child to be added.
|
|
530
|
+
* @returns Location of new ContentItem created. */
|
|
531
|
+
addItem(itemConfig: RowOrColumnItemConfig | StackItemConfig | ComponentItemConfig): LayoutManager.Location {
|
|
532
|
+
const location = this.addItemAtLocation(itemConfig);
|
|
533
|
+
if (location === undefined) {
|
|
534
|
+
throw new AssertError('LMAI99943');
|
|
535
|
+
} else {
|
|
536
|
+
return location;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/**
|
|
541
|
+
* Adds a ContentItem at the first valid selector location.
|
|
542
|
+
* @param itemConfig - ResolvedItemConfig of child to be added.
|
|
543
|
+
* @param locationSelectors - Array of location selectors used to find determine location in layout where ContentItem
|
|
544
|
+
* will be added. First location in array which is valid will be used. If undefined,
|
|
545
|
+
* {@link (LayoutManager:namespace).defaultLocationSelectors} will be used.
|
|
546
|
+
* @returns Location of new ContentItem created or undefined if no valid location selector was in array. */
|
|
547
|
+
addItemAtLocation(itemConfig: RowOrColumnItemConfig | StackItemConfig | ComponentItemConfig,
|
|
548
|
+
locationSelectors?: readonly LayoutManager.LocationSelector[]
|
|
549
|
+
): LayoutManager.Location | undefined {
|
|
550
|
+
if (this._groundItem === undefined) {
|
|
551
|
+
throw new Error('Cannot add component before init');
|
|
552
|
+
} else {
|
|
553
|
+
if (locationSelectors === undefined) {
|
|
554
|
+
// defaultLocationSelectors should always find a location
|
|
555
|
+
locationSelectors = LayoutManager.defaultLocationSelectors;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
const location = this.findFirstLocation(locationSelectors);
|
|
559
|
+
if (location === undefined) {
|
|
560
|
+
return undefined;
|
|
561
|
+
} else {
|
|
562
|
+
let parentItem = location.parentItem;
|
|
563
|
+
let addIdx: number;
|
|
564
|
+
switch (parentItem.type) {
|
|
565
|
+
case ItemType.ground: {
|
|
566
|
+
const groundItem = parentItem as GroundItem;
|
|
567
|
+
addIdx = groundItem.addItem(itemConfig, location.index);
|
|
568
|
+
if (addIdx >= 0) {
|
|
569
|
+
parentItem = this._groundItem.contentItems[0]; // was added to rootItem
|
|
570
|
+
} else {
|
|
571
|
+
addIdx = 0; // was added as rootItem (which is the first and only ContentItem in GroundItem)
|
|
572
|
+
}
|
|
573
|
+
break;
|
|
574
|
+
}
|
|
575
|
+
case ItemType.row:
|
|
576
|
+
case ItemType.column: {
|
|
577
|
+
const rowOrColumn = parentItem as RowOrColumn;
|
|
578
|
+
addIdx = rowOrColumn.addItem(itemConfig, location.index);
|
|
579
|
+
break;
|
|
580
|
+
}
|
|
581
|
+
case ItemType.stack: {
|
|
582
|
+
if (!ItemConfig.isComponent(itemConfig)) {
|
|
583
|
+
throw Error(i18nStrings[I18nStringId.ItemConfigIsNotTypeComponent]);
|
|
584
|
+
} else {
|
|
585
|
+
const stack = parentItem as Stack;
|
|
586
|
+
addIdx = stack.addItem(itemConfig, location.index);
|
|
587
|
+
break;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
case ItemType.component: {
|
|
591
|
+
throw new AssertError('LMAIALC87444602');
|
|
592
|
+
}
|
|
593
|
+
default:
|
|
594
|
+
throw new UnreachableCaseError('LMAIALU98881733', parentItem.type);
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
if (ItemConfig.isComponent(itemConfig)) {
|
|
598
|
+
// see if stack was inserted
|
|
599
|
+
const item = parentItem.contentItems[addIdx];
|
|
600
|
+
if (ContentItem.isStack(item)) {
|
|
601
|
+
parentItem = item;
|
|
602
|
+
addIdx = 0;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
location.parentItem = parentItem;
|
|
607
|
+
location.index = addIdx;
|
|
608
|
+
|
|
609
|
+
return location;
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/** Loads the specified component ResolvedItemConfig as root.
|
|
615
|
+
* This can be used to display a Component all by itself. The layout cannot be changed other than having another new layout loaded.
|
|
616
|
+
* Note that, if this layout is saved and reloaded, it will reload with the Component as a child of a Stack.
|
|
617
|
+
*/
|
|
618
|
+
loadComponentAsRoot(itemConfig: ComponentItemConfig): void {
|
|
619
|
+
if (this._groundItem === undefined) {
|
|
620
|
+
throw new Error('Cannot add item before init');
|
|
621
|
+
} else {
|
|
622
|
+
this._groundItem.loadComponentAsRoot(itemConfig);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
/** @deprecated Use {@link (LayoutManager:class).setSize} */
|
|
627
|
+
updateSize(width: number, height: number): void {
|
|
628
|
+
this.setSize(width, height);
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
/**
|
|
632
|
+
* Updates the layout managers size
|
|
633
|
+
*
|
|
634
|
+
* @param width - Width in pixels
|
|
635
|
+
* @param height - Height in pixels
|
|
636
|
+
*/
|
|
637
|
+
setSize(width: number, height: number): void {
|
|
638
|
+
this._width = width;
|
|
639
|
+
this._height = height;
|
|
640
|
+
|
|
641
|
+
if (this._isInitialised === true) {
|
|
642
|
+
if (this._groundItem === undefined) {
|
|
643
|
+
throw new UnexpectedUndefinedError('LMUS18881');
|
|
644
|
+
} else {
|
|
645
|
+
this._groundItem.setSize(this._width, this._height);
|
|
646
|
+
|
|
647
|
+
if (this._maximisedStack) {
|
|
648
|
+
const { width, height } = getElementWidthAndHeight(this._containerElement);
|
|
649
|
+
setElementWidth(this._maximisedStack.element, width);
|
|
650
|
+
setElementHeight(this._maximisedStack.element, height);
|
|
651
|
+
this._maximisedStack.updateSize(false);
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
this.adjustColumnsResponsive();
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
/** @internal */
|
|
660
|
+
beginSizeInvalidation(): void {
|
|
661
|
+
this._sizeInvalidationBeginCount++;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
/** @internal */
|
|
665
|
+
endSizeInvalidation(): void {
|
|
666
|
+
if (--this._sizeInvalidationBeginCount === 0) {
|
|
667
|
+
this.updateSizeFromContainer();
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
/** @internal */
|
|
672
|
+
updateSizeFromContainer(): void {
|
|
673
|
+
const { width, height } = getElementWidthAndHeight(this._containerElement);
|
|
674
|
+
this.setSize(width, height);
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
/**
|
|
678
|
+
* Update the size of the root ContentItem. This will update the size of all contentItems in the tree
|
|
679
|
+
* @param force - In some cases the size is not updated if it has not changed. In this case, events
|
|
680
|
+
* (such as ComponentContainer.virtualRectingRequiredEvent) are not fired. Setting force to true, ensures the size is updated regardless, and
|
|
681
|
+
* the respective events are fired. This is sometimes necessary when a component's size has not changed but it has become visible, and the
|
|
682
|
+
* relevant events need to be fired.
|
|
683
|
+
*/
|
|
684
|
+
updateRootSize(force = false): void {
|
|
685
|
+
if (this._groundItem === undefined) {
|
|
686
|
+
throw new UnexpectedUndefinedError('LMURS28881');
|
|
687
|
+
} else {
|
|
688
|
+
this._groundItem.updateSize(force);
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
/** @public */
|
|
693
|
+
createAndInitContentItem(config: ResolvedItemConfig, parent: ContentItem): ContentItem {
|
|
694
|
+
const newItem = this.createContentItem(config, parent);
|
|
695
|
+
newItem.init();
|
|
696
|
+
return newItem;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
/**
|
|
700
|
+
* Recursively creates new item tree structures based on a provided
|
|
701
|
+
* ItemConfiguration object
|
|
702
|
+
*
|
|
703
|
+
* @param config - ResolvedItemConfig
|
|
704
|
+
* @param parent - The item the newly created item should be a child of
|
|
705
|
+
* @internal
|
|
706
|
+
*/
|
|
707
|
+
createContentItem(config: ResolvedItemConfig, parent: ContentItem): ContentItem {
|
|
708
|
+
if (typeof config.type !== 'string') {
|
|
709
|
+
throw new ConfigurationError('Missing parameter \'type\'', JSON.stringify(config));
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
/**
|
|
713
|
+
* We add an additional stack around every component that's not within a stack anyways.
|
|
714
|
+
*/
|
|
715
|
+
if (
|
|
716
|
+
// If this is a component
|
|
717
|
+
ResolvedItemConfig.isComponentItem(config) &&
|
|
718
|
+
|
|
719
|
+
// and it's not already within a stack
|
|
720
|
+
!(parent instanceof Stack) &&
|
|
721
|
+
|
|
722
|
+
// and we have a parent
|
|
723
|
+
!!parent &&
|
|
724
|
+
|
|
725
|
+
// and it's not the topmost item in a new window
|
|
726
|
+
!(this.isSubWindow === true && parent instanceof GroundItem)
|
|
727
|
+
) {
|
|
728
|
+
const stackConfig: ResolvedStackItemConfig = {
|
|
729
|
+
type: ItemType.stack,
|
|
730
|
+
content: [config],
|
|
731
|
+
size: config.size,
|
|
732
|
+
sizeUnit: config.sizeUnit,
|
|
733
|
+
minSize: config.minSize,
|
|
734
|
+
minSizeUnit: config.minSizeUnit,
|
|
735
|
+
id: config.id,
|
|
736
|
+
maximised: config.maximised,
|
|
737
|
+
isClosable: config.isClosable,
|
|
738
|
+
activeItemIndex: 0,
|
|
739
|
+
header: undefined,
|
|
740
|
+
};
|
|
741
|
+
|
|
742
|
+
config = stackConfig;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
const contentItem = this.createContentItemFromConfig(config, parent);
|
|
746
|
+
return contentItem;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
findFirstComponentItemById(id: string): ComponentItem | undefined {
|
|
750
|
+
if (this._groundItem === undefined) {
|
|
751
|
+
throw new UnexpectedUndefinedError('LMFFCIBI82446');
|
|
752
|
+
} else {
|
|
753
|
+
return this.findFirstContentItemTypeByIdRecursive(ItemType.component, id, this._groundItem) as ComponentItem;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
/**
|
|
758
|
+
* Creates a popout window with the specified content at the specified position
|
|
759
|
+
*
|
|
760
|
+
* @param itemConfigOrContentItem - The content of the popout window's layout manager derived from either
|
|
761
|
+
* a {@link (ContentItem:class)} or {@link (ItemConfig:interface)} or ResolvedItemConfig content (array of {@link (ItemConfig:interface)})
|
|
762
|
+
* @param positionAndSize - The width, height, left and top of Popout window
|
|
763
|
+
* @param parentId -The id of the element this item will be appended to when popIn is called
|
|
764
|
+
* @param indexInParent - The position of this item within its parent element
|
|
765
|
+
*/
|
|
766
|
+
|
|
767
|
+
createPopout(itemConfigOrContentItem: ContentItem | ResolvedRootItemConfig,
|
|
768
|
+
positionAndSize: ResolvedPopoutLayoutConfig.Window,
|
|
769
|
+
parentId: string | null,
|
|
770
|
+
indexInParent: number | null
|
|
771
|
+
): BrowserPopout {
|
|
772
|
+
if (itemConfigOrContentItem instanceof ContentItem) {
|
|
773
|
+
return this.createPopoutFromContentItem(itemConfigOrContentItem, positionAndSize, parentId, indexInParent);
|
|
774
|
+
} else {
|
|
775
|
+
return this.createPopoutFromItemConfig(itemConfigOrContentItem, positionAndSize, parentId, indexInParent);
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
/** @internal */
|
|
780
|
+
createPopoutFromContentItem(item: ContentItem,
|
|
781
|
+
window: ResolvedPopoutLayoutConfig.Window | undefined,
|
|
782
|
+
parentId: string | null,
|
|
783
|
+
indexInParent: number | null | undefined,
|
|
784
|
+
): BrowserPopout {
|
|
785
|
+
/**
|
|
786
|
+
* If the item is the only component within a stack or for some
|
|
787
|
+
* other reason the only child of its parent the parent will be destroyed
|
|
788
|
+
* when the child is removed.
|
|
789
|
+
*
|
|
790
|
+
* In order to support this we move up the tree until we find something
|
|
791
|
+
* that will remain after the item is being popped out
|
|
792
|
+
*/
|
|
793
|
+
let parent = item.parent;
|
|
794
|
+
let child = item;
|
|
795
|
+
while (parent !== null && parent.contentItems.length === 1 && !parent.isGround) {
|
|
796
|
+
child = parent;
|
|
797
|
+
parent = parent.parent;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
if (parent === null) {
|
|
801
|
+
throw new UnexpectedNullError('LMCPFCI00834');
|
|
802
|
+
} else {
|
|
803
|
+
if (indexInParent === undefined) {
|
|
804
|
+
indexInParent = parent.contentItems.indexOf(child);
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
if (parentId !== null) {
|
|
808
|
+
parent.addPopInParentId(parentId);
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
if (window === undefined) {
|
|
812
|
+
const windowLeft = globalThis.screenX || globalThis.screenLeft;
|
|
813
|
+
const windowTop = globalThis.screenY || globalThis.screenTop;
|
|
814
|
+
const offsetLeft = item.element.offsetLeft;
|
|
815
|
+
const offsetTop = item.element.offsetTop
|
|
816
|
+
// const { left: offsetLeft, top: offsetTop } = getJQueryLeftAndTop(item.element);
|
|
817
|
+
const { width, height } = getElementWidthAndHeight(item.element);
|
|
818
|
+
|
|
819
|
+
window = {
|
|
820
|
+
left: windowLeft + offsetLeft,
|
|
821
|
+
top: windowTop + offsetTop,
|
|
822
|
+
width,
|
|
823
|
+
height,
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
const itemConfig = item.toConfig();
|
|
828
|
+
item.remove();
|
|
829
|
+
|
|
830
|
+
if (!ResolvedRootItemConfig.isRootItemConfig(itemConfig)) {
|
|
831
|
+
throw new Error(`${i18nStrings[I18nStringId.PopoutCannotBeCreatedWithGroundItemConfig]}`);
|
|
832
|
+
} else {
|
|
833
|
+
return this.createPopoutFromItemConfig(itemConfig, window, parentId, indexInParent);
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
/** @internal */
|
|
839
|
+
beginVirtualSizedContainerAdding(): void {
|
|
840
|
+
if (++this._virtualSizedContainerAddingBeginCount === 0) {
|
|
841
|
+
this._virtualSizedContainers.length = 0;
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
/** @internal */
|
|
846
|
+
addVirtualSizedContainer(container: ComponentContainer): void {
|
|
847
|
+
this._virtualSizedContainers.push(container);
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
/** @internal */
|
|
851
|
+
endVirtualSizedContainerAdding(): void {
|
|
852
|
+
if (--this._virtualSizedContainerAddingBeginCount === 0) {
|
|
853
|
+
const count = this._virtualSizedContainers.length;
|
|
854
|
+
if (count > 0) {
|
|
855
|
+
this.fireBeforeVirtualRectingEvent(count);
|
|
856
|
+
for (let i = 0; i < count; i++) {
|
|
857
|
+
const container = this._virtualSizedContainers[i];
|
|
858
|
+
container.notifyVirtualRectingRequired();
|
|
859
|
+
}
|
|
860
|
+
this.fireAfterVirtualRectingEvent();
|
|
861
|
+
this._virtualSizedContainers.length = 0;
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
/** @internal */
|
|
867
|
+
fireBeforeVirtualRectingEvent(count: number): void {
|
|
868
|
+
if (this.beforeVirtualRectingEvent !== undefined) {
|
|
869
|
+
this.beforeVirtualRectingEvent(count);
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
/** @internal */
|
|
874
|
+
fireAfterVirtualRectingEvent(): void {
|
|
875
|
+
if (this.afterVirtualRectingEvent !== undefined) {
|
|
876
|
+
this.afterVirtualRectingEvent();
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
/** @internal */
|
|
881
|
+
private createPopoutFromItemConfig(rootItemConfig: ResolvedRootItemConfig,
|
|
882
|
+
window: ResolvedPopoutLayoutConfig.Window,
|
|
883
|
+
parentId: string | null,
|
|
884
|
+
indexInParent: number | null
|
|
885
|
+
) {
|
|
886
|
+
const layoutConfig = this.toConfig();
|
|
887
|
+
|
|
888
|
+
const popoutLayoutConfig: ResolvedPopoutLayoutConfig = {
|
|
889
|
+
root: rootItemConfig,
|
|
890
|
+
openPopouts: [],
|
|
891
|
+
settings: layoutConfig.settings,
|
|
892
|
+
dimensions: layoutConfig.dimensions,
|
|
893
|
+
header: layoutConfig.header,
|
|
894
|
+
window,
|
|
895
|
+
parentId,
|
|
896
|
+
indexInParent,
|
|
897
|
+
resolved: true,
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
return this.createPopoutFromPopoutLayoutConfig(popoutLayoutConfig);
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
/** @internal */
|
|
904
|
+
createPopoutFromPopoutLayoutConfig(config: ResolvedPopoutLayoutConfig): BrowserPopout {
|
|
905
|
+
const configWindow = config.window;
|
|
906
|
+
const initialWindow: Rect = {
|
|
907
|
+
left: configWindow.left ?? (globalThis.screenX || globalThis.screenLeft + 20),
|
|
908
|
+
top: configWindow.top ?? (globalThis.screenY || globalThis.screenTop + 20),
|
|
909
|
+
width: configWindow.width ?? 500,
|
|
910
|
+
height: configWindow.height ?? 309,
|
|
911
|
+
};
|
|
912
|
+
|
|
913
|
+
const browserPopout = new BrowserPopout(config, initialWindow, this);
|
|
914
|
+
|
|
915
|
+
browserPopout.on('initialised', () => this.emit('windowOpened', browserPopout));
|
|
916
|
+
browserPopout.on('closed', () => this.reconcilePopoutWindows());
|
|
917
|
+
|
|
918
|
+
this._openPopouts.push(browserPopout);
|
|
919
|
+
|
|
920
|
+
if (this.layoutConfig.settings.closePopoutsOnUnload && !this._windowBeforeUnloadListening) {
|
|
921
|
+
globalThis.addEventListener('beforeunload', this._windowBeforeUnloadListener, { passive: true });
|
|
922
|
+
this._windowBeforeUnloadListening = true;
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
return browserPopout;
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
/**
|
|
929
|
+
* Closes all Open Popouts
|
|
930
|
+
* Applications can call this method when a page is unloaded to remove its open popouts
|
|
931
|
+
*/
|
|
932
|
+
|
|
933
|
+
closeAllOpenPopouts() {
|
|
934
|
+
for (let i = 0; i < this._openPopouts.length; i++) {
|
|
935
|
+
this._openPopouts[i].close();
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
this._openPopouts.length = 0;
|
|
939
|
+
|
|
940
|
+
if (this._windowBeforeUnloadListening) {
|
|
941
|
+
globalThis.removeEventListener('beforeunload', this._windowBeforeUnloadListener);
|
|
942
|
+
this._windowBeforeUnloadListening = false;
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
/**
|
|
947
|
+
* Attaches DragListener to any given DOM element
|
|
948
|
+
* and turns it into a way of creating new ComponentItems
|
|
949
|
+
* by 'dragging' the DOM element into the layout
|
|
950
|
+
*
|
|
951
|
+
* @param element - The HTML element which will be listened to for commencement of drag.
|
|
952
|
+
* @param componentTypeOrItemConfigCallback - Type of component to be created, or a callback which will provide the ItemConfig
|
|
953
|
+
* to be used to create the component.
|
|
954
|
+
* @param componentState - Optional initial state of component. This will be ignored if componentTypeOrFtn is a function.
|
|
955
|
+
*
|
|
956
|
+
* @returns an opaque object that identifies the DOM element
|
|
957
|
+
* and the attached itemConfig. This can be used in
|
|
958
|
+
* removeDragSource() later to get rid of the drag listeners.
|
|
959
|
+
*/
|
|
960
|
+
newDragSource(element: HTMLElement, itemConfigCallback: () => (DragSource.ComponentItemConfig | ComponentItemConfig)): DragSource;
|
|
961
|
+
/** @deprecated will be replaced in version 3 with newDragSource(element: HTMLElement, itemConfig: ComponentItemConfig) */
|
|
962
|
+
newDragSource(element: HTMLElement, componentType: JsonValue, componentState?: JsonValue, title?: JsonValue, id?: string): DragSource;
|
|
963
|
+
newDragSource(element: HTMLElement,
|
|
964
|
+
componentTypeOrItemConfigCallback: JsonValue | (() => (DragSource.ComponentItemConfig | ComponentItemConfig)),
|
|
965
|
+
componentState?: JsonValue,
|
|
966
|
+
title?: string,
|
|
967
|
+
id?: string,
|
|
968
|
+
): DragSource {
|
|
969
|
+
const dragSource = new DragSource(this, element, [], componentTypeOrItemConfigCallback, componentState, title, id);
|
|
970
|
+
this._dragSources.push(dragSource);
|
|
971
|
+
|
|
972
|
+
return dragSource;
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
/**
|
|
976
|
+
* Removes a DragListener added by createDragSource() so the corresponding
|
|
977
|
+
* DOM element is not a drag source any more.
|
|
978
|
+
*/
|
|
979
|
+
removeDragSource(dragSource: DragSource): void {
|
|
980
|
+
removeFromArray(dragSource, this._dragSources );
|
|
981
|
+
dragSource.destroy();
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
/** @internal */
|
|
985
|
+
startComponentDrag(x: number, y: number, dragListener: DragListener, componentItem: ComponentItem, stack: Stack): void {
|
|
986
|
+
new DragProxy(
|
|
987
|
+
x,
|
|
988
|
+
y,
|
|
989
|
+
dragListener,
|
|
990
|
+
this,
|
|
991
|
+
componentItem,
|
|
992
|
+
stack
|
|
993
|
+
);
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
/**
|
|
997
|
+
* Programmatically focuses an item. This focuses the specified component item
|
|
998
|
+
* and the item emits a focus event
|
|
999
|
+
*
|
|
1000
|
+
* @param item - The component item to be focused
|
|
1001
|
+
* @param suppressEvent - Whether to emit focus event
|
|
1002
|
+
*/
|
|
1003
|
+
focusComponent(item: ComponentItem, suppressEvent = false): void {
|
|
1004
|
+
item.focus(suppressEvent);
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
/**
|
|
1008
|
+
* Programmatically blurs (defocuses) the currently focused component.
|
|
1009
|
+
* If a component item is focused, then it is blurred and and the item emits a blur event
|
|
1010
|
+
*
|
|
1011
|
+
* @param item - The component item to be blurred
|
|
1012
|
+
* @param suppressEvent - Whether to emit blur event
|
|
1013
|
+
*/
|
|
1014
|
+
clearComponentFocus(suppressEvent = false): void {
|
|
1015
|
+
this.setFocusedComponentItem(undefined, suppressEvent);
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
/**
|
|
1019
|
+
* Programmatically focuses a component item or removes focus (blurs) from an existing focused component item.
|
|
1020
|
+
*
|
|
1021
|
+
* @param item - If defined, specifies the component item to be given focus. If undefined, clear component focus.
|
|
1022
|
+
* @param suppressEvents - Whether to emit focus and blur events
|
|
1023
|
+
* @internal
|
|
1024
|
+
*/
|
|
1025
|
+
setFocusedComponentItem(item: ComponentItem | undefined, suppressEvents = false): void {
|
|
1026
|
+
if (item !== this._focusedComponentItem) {
|
|
1027
|
+
|
|
1028
|
+
let newFocusedParentItem: ComponentParentableItem | undefined;
|
|
1029
|
+
if (item === undefined) {
|
|
1030
|
+
newFocusedParentItem === undefined;
|
|
1031
|
+
} else {
|
|
1032
|
+
newFocusedParentItem = item.parentItem;
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
if (this._focusedComponentItem !== undefined) {
|
|
1036
|
+
const oldFocusedItem = this._focusedComponentItem;
|
|
1037
|
+
this._focusedComponentItem = undefined;
|
|
1038
|
+
oldFocusedItem.setBlurred(suppressEvents);
|
|
1039
|
+
const oldFocusedParentItem = oldFocusedItem.parentItem;
|
|
1040
|
+
if (newFocusedParentItem === oldFocusedParentItem) {
|
|
1041
|
+
newFocusedParentItem = undefined;
|
|
1042
|
+
} else {
|
|
1043
|
+
oldFocusedParentItem.setFocusedValue(false);
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
if (item !== undefined) {
|
|
1048
|
+
this._focusedComponentItem = item;
|
|
1049
|
+
item.setFocused(suppressEvents);
|
|
1050
|
+
if (newFocusedParentItem !== undefined) {
|
|
1051
|
+
newFocusedParentItem.setFocusedValue(true);
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
/** @internal */
|
|
1058
|
+
private createContentItemFromConfig(config: ResolvedItemConfig, parent: ContentItem): ContentItem {
|
|
1059
|
+
switch (config.type) {
|
|
1060
|
+
case ItemType.ground: throw new AssertError('LMCCIFC68871');
|
|
1061
|
+
case ItemType.row: return new RowOrColumn(false, this, config as ResolvedRowOrColumnItemConfig, parent);
|
|
1062
|
+
case ItemType.column: return new RowOrColumn(true, this, config as ResolvedRowOrColumnItemConfig, parent);
|
|
1063
|
+
case ItemType.stack: return new Stack(this, config as ResolvedStackItemConfig, parent);
|
|
1064
|
+
case ItemType.component:
|
|
1065
|
+
return new ComponentItem(this, config as ResolvedComponentItemConfig, parent as Stack);
|
|
1066
|
+
default:
|
|
1067
|
+
throw new UnreachableCaseError('CCC913564', config.type, 'Invalid Config Item type specified');
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
/**
|
|
1072
|
+
* This should only be called from stack component.
|
|
1073
|
+
* Stack will look after docking processing associated with maximise/minimise
|
|
1074
|
+
* @internal
|
|
1075
|
+
**/
|
|
1076
|
+
setMaximisedStack(stack: Stack | undefined): void {
|
|
1077
|
+
if (stack === undefined) {
|
|
1078
|
+
if (this._maximisedStack !== undefined) {
|
|
1079
|
+
this.processMinimiseMaximisedStack();
|
|
1080
|
+
}
|
|
1081
|
+
} else {
|
|
1082
|
+
if (stack !== this._maximisedStack) {
|
|
1083
|
+
if (this._maximisedStack !== undefined) {
|
|
1084
|
+
this.processMinimiseMaximisedStack();
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
this.processMaximiseStack(stack);
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
checkMinimiseMaximisedStack(): void {
|
|
1093
|
+
if (this._maximisedStack !== undefined) {
|
|
1094
|
+
this._maximisedStack.minimise();
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
// showAllActiveContentItems() was called from ContentItem.show(). Not sure what its purpose was so have commented out
|
|
1099
|
+
// Everything seems to work ok without this. Have left commented code just in case there was a reason for it becomes
|
|
1100
|
+
// apparent
|
|
1101
|
+
|
|
1102
|
+
// /** @internal */
|
|
1103
|
+
// showAllActiveContentItems(): void {
|
|
1104
|
+
// const allStacks = this.getAllStacks();
|
|
1105
|
+
|
|
1106
|
+
// for (let i = 0; i < allStacks.length; i++) {
|
|
1107
|
+
// const stack = allStacks[i];
|
|
1108
|
+
// const activeContentItem = stack.getActiveComponentItem();
|
|
1109
|
+
|
|
1110
|
+
// if (activeContentItem !== undefined) {
|
|
1111
|
+
// if (!(activeContentItem instanceof ComponentItem)) {
|
|
1112
|
+
// throw new AssertError('LMSAACIS22298');
|
|
1113
|
+
// } else {
|
|
1114
|
+
// activeContentItem.container.show();
|
|
1115
|
+
// }
|
|
1116
|
+
// }
|
|
1117
|
+
// }
|
|
1118
|
+
// }
|
|
1119
|
+
|
|
1120
|
+
// hideAllActiveContentItems() was called from ContentItem.hide(). Not sure what its purpose was so have commented out
|
|
1121
|
+
// Everything seems to work ok without this. Have left commented code just in case there was a reason for it becomes
|
|
1122
|
+
// apparent
|
|
1123
|
+
|
|
1124
|
+
// /** @internal */
|
|
1125
|
+
// hideAllActiveContentItems(): void {
|
|
1126
|
+
// const allStacks = this.getAllStacks();
|
|
1127
|
+
|
|
1128
|
+
// for (let i = 0; i < allStacks.length; i++) {
|
|
1129
|
+
// const stack = allStacks[i];
|
|
1130
|
+
// const activeContentItem = stack.getActiveComponentItem();
|
|
1131
|
+
|
|
1132
|
+
// if (activeContentItem !== undefined) {
|
|
1133
|
+
// if (!(activeContentItem instanceof ComponentItem)) {
|
|
1134
|
+
// throw new AssertError('LMSAACIH22298');
|
|
1135
|
+
// } else {
|
|
1136
|
+
// activeContentItem.container.hide();
|
|
1137
|
+
// }
|
|
1138
|
+
// }
|
|
1139
|
+
// }
|
|
1140
|
+
// }
|
|
1141
|
+
|
|
1142
|
+
/** @internal */
|
|
1143
|
+
private cleanupBeforeMaximisedStackDestroyed(event: EventEmitter.BubblingEvent) {
|
|
1144
|
+
if (this._maximisedStack !== null && this._maximisedStack === event.target) {
|
|
1145
|
+
this._maximisedStack.off('beforeItemDestroyed', this._maximisedStackBeforeDestroyedListener);
|
|
1146
|
+
this._maximisedStack = undefined;
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
/**
|
|
1151
|
+
* This method is used to get around sandboxed iframe restrictions.
|
|
1152
|
+
* If 'allow-top-navigation' is not specified in the iframe's 'sandbox' attribute
|
|
1153
|
+
* (as is the case with codepens) the parent window is forbidden from calling certain
|
|
1154
|
+
* methods on the child, such as window.close() or setting document.location.href.
|
|
1155
|
+
*
|
|
1156
|
+
* This prevented GoldenLayout popouts from popping in in codepens. The fix is to call
|
|
1157
|
+
* _$closeWindow on the child window's gl instance which (after a timeout to disconnect
|
|
1158
|
+
* the invoking method from the close call) closes itself.
|
|
1159
|
+
*
|
|
1160
|
+
* @internal
|
|
1161
|
+
*/
|
|
1162
|
+
closeWindow(): void {
|
|
1163
|
+
globalThis.setTimeout(() => globalThis.close(), 1);
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
/** @internal */
|
|
1167
|
+
getArea(x: number, y: number): ContentItem.Area | null {
|
|
1168
|
+
let matchingArea = null;
|
|
1169
|
+
let smallestSurface = Infinity;
|
|
1170
|
+
|
|
1171
|
+
for (let i = 0; i < this._itemAreas.length; i++) {
|
|
1172
|
+
const area = this._itemAreas[i];
|
|
1173
|
+
|
|
1174
|
+
if (
|
|
1175
|
+
x >= area.x1 &&
|
|
1176
|
+
x < area.x2 && // x2 is not included in area
|
|
1177
|
+
y >= area.y1 &&
|
|
1178
|
+
y < area.y2 && // y2 is not included in area
|
|
1179
|
+
smallestSurface > area.surface
|
|
1180
|
+
) {
|
|
1181
|
+
smallestSurface = area.surface;
|
|
1182
|
+
matchingArea = area;
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
return matchingArea;
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
/** @internal */
|
|
1190
|
+
calculateItemAreas(): void {
|
|
1191
|
+
const allContentItems = this.getAllContentItems();
|
|
1192
|
+
/**
|
|
1193
|
+
* If the last item is dragged out, highlight the entire container size to
|
|
1194
|
+
* allow to re-drop it. this.ground.contentiItems.length === 0 at this point
|
|
1195
|
+
*
|
|
1196
|
+
* Don't include ground into the possible drop areas though otherwise since it
|
|
1197
|
+
* will used for every gap in the layout, e.g. splitters
|
|
1198
|
+
*/
|
|
1199
|
+
const groundItem = this._groundItem;
|
|
1200
|
+
if (groundItem === undefined) {
|
|
1201
|
+
throw new UnexpectedUndefinedError('LMCIAR44365');
|
|
1202
|
+
} else {
|
|
1203
|
+
if (allContentItems.length === 1) {
|
|
1204
|
+
// No root ContentItem (just Ground ContentItem)
|
|
1205
|
+
const groundArea = groundItem.getElementArea();
|
|
1206
|
+
if (groundArea === null) {
|
|
1207
|
+
throw new UnexpectedNullError('LMCIARA44365')
|
|
1208
|
+
} else {
|
|
1209
|
+
this._itemAreas = [groundArea];
|
|
1210
|
+
}
|
|
1211
|
+
return;
|
|
1212
|
+
} else {
|
|
1213
|
+
if (groundItem.contentItems[0].isStack) {
|
|
1214
|
+
// if root is Stack, then split stack and sides of Layout are same, so skip sides
|
|
1215
|
+
this._itemAreas = [];
|
|
1216
|
+
} else {
|
|
1217
|
+
// sides of layout
|
|
1218
|
+
this._itemAreas = groundItem.createSideAreas();
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
for (let i = 0; i < allContentItems.length; i++) {
|
|
1222
|
+
const stack = allContentItems[i];
|
|
1223
|
+
if (ContentItem.isStack(stack)) {
|
|
1224
|
+
const area = stack.getArea();
|
|
1225
|
+
|
|
1226
|
+
if (area === null) {
|
|
1227
|
+
continue;
|
|
1228
|
+
} else {
|
|
1229
|
+
this._itemAreas.push(area);
|
|
1230
|
+
const stackContentAreaDimensions = stack.contentAreaDimensions;
|
|
1231
|
+
if (stackContentAreaDimensions === undefined) {
|
|
1232
|
+
throw new UnexpectedUndefinedError('LMCIASC45599');
|
|
1233
|
+
} else {
|
|
1234
|
+
const highlightArea = stackContentAreaDimensions.header.highlightArea
|
|
1235
|
+
const surface = (highlightArea.x2 - highlightArea.x1) * (highlightArea.y2 - highlightArea.y1);
|
|
1236
|
+
|
|
1237
|
+
const header: ContentItem.Area = {
|
|
1238
|
+
x1: highlightArea.x1,
|
|
1239
|
+
x2: highlightArea.x2,
|
|
1240
|
+
y1: highlightArea.y1,
|
|
1241
|
+
y2: highlightArea.y2,
|
|
1242
|
+
contentItem: stack,
|
|
1243
|
+
surface,
|
|
1244
|
+
};
|
|
1245
|
+
this._itemAreas.push(header);
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
/**
|
|
1255
|
+
* Called as part of loading a new layout (including initial init()).
|
|
1256
|
+
* Checks to see layout has a maximised item. If so, it maximises that item.
|
|
1257
|
+
* @internal
|
|
1258
|
+
*/
|
|
1259
|
+
private checkLoadedLayoutMaximiseItem() {
|
|
1260
|
+
if (this._groundItem === undefined) {
|
|
1261
|
+
throw new UnexpectedUndefinedError('LMCLLMI43432');
|
|
1262
|
+
} else {
|
|
1263
|
+
const configMaximisedItems = this._groundItem.getConfigMaximisedItems();
|
|
1264
|
+
|
|
1265
|
+
if (configMaximisedItems.length > 0) {
|
|
1266
|
+
let item = configMaximisedItems[0];
|
|
1267
|
+
if (ContentItem.isComponentItem(item)) {
|
|
1268
|
+
const stack = item.parent;
|
|
1269
|
+
if (stack === null) {
|
|
1270
|
+
throw new UnexpectedNullError('LMXLLMI69999');
|
|
1271
|
+
} else {
|
|
1272
|
+
item = stack;
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
if (!ContentItem.isStack(item)) {
|
|
1276
|
+
throw new AssertError('LMCLLMI19993');
|
|
1277
|
+
} else {
|
|
1278
|
+
item.maximise();
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
/** @internal */
|
|
1285
|
+
private processMaximiseStack(stack: Stack): void {
|
|
1286
|
+
this._maximisedStack = stack;
|
|
1287
|
+
stack.on('beforeItemDestroyed', this._maximisedStackBeforeDestroyedListener);
|
|
1288
|
+
stack.element.classList.add(DomConstants.ClassName.Maximised);
|
|
1289
|
+
stack.element.insertAdjacentElement('afterend', this._maximisePlaceholder);
|
|
1290
|
+
if (this._groundItem === undefined) {
|
|
1291
|
+
throw new UnexpectedUndefinedError('LMMXI19993');
|
|
1292
|
+
} else {
|
|
1293
|
+
this._groundItem.element.prepend(stack.element);
|
|
1294
|
+
const { width, height } = getElementWidthAndHeight(this._containerElement);
|
|
1295
|
+
setElementWidth(stack.element, width);
|
|
1296
|
+
setElementHeight(stack.element, height);
|
|
1297
|
+
stack.updateSize(true);
|
|
1298
|
+
stack.focusActiveContentItem();
|
|
1299
|
+
this._maximisedStack.emit('maximised');
|
|
1300
|
+
this.emit('stateChanged');
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
/** @internal */
|
|
1305
|
+
private processMinimiseMaximisedStack(): void {
|
|
1306
|
+
if (this._maximisedStack === undefined) {
|
|
1307
|
+
throw new AssertError('LMMMS74422');
|
|
1308
|
+
} else {
|
|
1309
|
+
const stack = this._maximisedStack;
|
|
1310
|
+
if (stack.parent === null) {
|
|
1311
|
+
throw new UnexpectedNullError('LMMI13668');
|
|
1312
|
+
} else {
|
|
1313
|
+
stack.element.classList.remove(DomConstants.ClassName.Maximised);
|
|
1314
|
+
this._maximisePlaceholder.insertAdjacentElement('afterend', stack.element);
|
|
1315
|
+
this._maximisePlaceholder.remove();
|
|
1316
|
+
this.updateRootSize(true);
|
|
1317
|
+
this._maximisedStack = undefined;
|
|
1318
|
+
stack.off('beforeItemDestroyed', this._maximisedStackBeforeDestroyedListener);
|
|
1319
|
+
stack.emit('minimised');
|
|
1320
|
+
this.emit('stateChanged');
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
/**
|
|
1326
|
+
* Iterates through the array of open popout windows and removes the ones
|
|
1327
|
+
* that are effectively closed. This is necessary due to the lack of reliably
|
|
1328
|
+
* listening for window.close / unload events in a cross browser compatible fashion.
|
|
1329
|
+
* @internal
|
|
1330
|
+
*/
|
|
1331
|
+
private reconcilePopoutWindows() {
|
|
1332
|
+
const openPopouts: BrowserPopout[] = [];
|
|
1333
|
+
|
|
1334
|
+
for (let i = 0; i < this._openPopouts.length; i++) {
|
|
1335
|
+
if (this._openPopouts[i].getWindow().closed === false) {
|
|
1336
|
+
openPopouts.push(this._openPopouts[i]);
|
|
1337
|
+
} else {
|
|
1338
|
+
this.emit('windowClosed', this._openPopouts[i]);
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
if (this._openPopouts.length !== openPopouts.length) {
|
|
1343
|
+
this._openPopouts = openPopouts;
|
|
1344
|
+
this.emit('stateChanged');
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
/**
|
|
1350
|
+
* Returns a flattened array of all content items,
|
|
1351
|
+
* regardles of level or type
|
|
1352
|
+
* @internal
|
|
1353
|
+
*/
|
|
1354
|
+
private getAllContentItems() {
|
|
1355
|
+
if (this._groundItem === undefined) {
|
|
1356
|
+
throw new UnexpectedUndefinedError('LMGACI13130');
|
|
1357
|
+
} else {
|
|
1358
|
+
return this._groundItem.getAllContentItems();
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
/**
|
|
1363
|
+
* Creates Subwindows (if there are any). Throws an error
|
|
1364
|
+
* if popouts are blocked.
|
|
1365
|
+
* @internal
|
|
1366
|
+
*/
|
|
1367
|
+
private createSubWindows() {
|
|
1368
|
+
for (let i = 0; i < this.layoutConfig.openPopouts.length; i++) {
|
|
1369
|
+
const popoutConfig = this.layoutConfig.openPopouts[i];
|
|
1370
|
+
this.createPopoutFromPopoutLayoutConfig(popoutConfig);
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
/**
|
|
1375
|
+
* Debounces resize events
|
|
1376
|
+
* @internal
|
|
1377
|
+
*/
|
|
1378
|
+
private handleContainerResize(): void {
|
|
1379
|
+
if (this.resizeWithContainerAutomatically) {
|
|
1380
|
+
this.processResizeWithDebounce();
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
/**
|
|
1385
|
+
* Debounces resize events
|
|
1386
|
+
* @internal
|
|
1387
|
+
*/
|
|
1388
|
+
private processResizeWithDebounce(): void {
|
|
1389
|
+
if (this.resizeDebounceExtendedWhenPossible) {
|
|
1390
|
+
this.checkClearResizeTimeout();
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
if (this._resizeTimeoutId === undefined) {
|
|
1394
|
+
this._resizeTimeoutId = setTimeout(
|
|
1395
|
+
() => {
|
|
1396
|
+
this._resizeTimeoutId = undefined;
|
|
1397
|
+
this.beginSizeInvalidation();
|
|
1398
|
+
this.endSizeInvalidation();
|
|
1399
|
+
},
|
|
1400
|
+
this.resizeDebounceInterval,
|
|
1401
|
+
);
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
private checkClearResizeTimeout() {
|
|
1406
|
+
if (this._resizeTimeoutId !== undefined) {
|
|
1407
|
+
clearTimeout(this._resizeTimeoutId);
|
|
1408
|
+
this._resizeTimeoutId = undefined;
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
/**
|
|
1413
|
+
* Determines what element the layout will be created in
|
|
1414
|
+
* @internal
|
|
1415
|
+
*/
|
|
1416
|
+
private setContainer() {
|
|
1417
|
+
const bodyElement = document.body;
|
|
1418
|
+
const containerElement = this._containerElement ?? bodyElement;
|
|
1419
|
+
|
|
1420
|
+
if (containerElement === bodyElement) {
|
|
1421
|
+
this.resizeWithContainerAutomatically = true;
|
|
1422
|
+
|
|
1423
|
+
const documentElement = document.documentElement;
|
|
1424
|
+
documentElement.style.height = '100%';
|
|
1425
|
+
documentElement.style.margin = '0';
|
|
1426
|
+
documentElement.style.padding = '0';
|
|
1427
|
+
documentElement.style.overflow = 'clip';
|
|
1428
|
+
bodyElement.style.height = '100%';
|
|
1429
|
+
bodyElement.style.margin = '0';
|
|
1430
|
+
bodyElement.style.padding = '0';
|
|
1431
|
+
bodyElement.style.overflow = 'clip';
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
this._containerElement = containerElement;
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
/**
|
|
1438
|
+
* Called when the window is closed or the user navigates away
|
|
1439
|
+
* from the page
|
|
1440
|
+
* @internal
|
|
1441
|
+
* @deprecated to be removed in version 3
|
|
1442
|
+
*/
|
|
1443
|
+
private onBeforeUnload(): void {
|
|
1444
|
+
this.destroy();
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
/**
|
|
1448
|
+
* Adjusts the number of columns to be lower to fit the screen and still maintain minItemWidth.
|
|
1449
|
+
* @internal
|
|
1450
|
+
*/
|
|
1451
|
+
private adjustColumnsResponsive() {
|
|
1452
|
+
if (this._groundItem === undefined) {
|
|
1453
|
+
throw new UnexpectedUndefinedError('LMACR20883');
|
|
1454
|
+
} else {
|
|
1455
|
+
this._firstLoad = false;
|
|
1456
|
+
// If there is no min width set, or not content items, do nothing.
|
|
1457
|
+
if (this.useResponsiveLayout() &&
|
|
1458
|
+
!this._updatingColumnsResponsive &&
|
|
1459
|
+
this._groundItem.contentItems.length > 0 &&
|
|
1460
|
+
this._groundItem.contentItems[0].isRow)
|
|
1461
|
+
{
|
|
1462
|
+
if (this._groundItem === undefined || this._width === null) {
|
|
1463
|
+
throw new UnexpectedUndefinedError('LMACR77412');
|
|
1464
|
+
} else {
|
|
1465
|
+
// If there is only one column, do nothing.
|
|
1466
|
+
const columnCount = this._groundItem.contentItems[0].contentItems.length;
|
|
1467
|
+
if (columnCount <= 1) {
|
|
1468
|
+
return;
|
|
1469
|
+
} else {
|
|
1470
|
+
// If they all still fit, do nothing.
|
|
1471
|
+
const minItemWidth = this.layoutConfig.dimensions.defaultMinItemWidth;
|
|
1472
|
+
const totalMinWidth = columnCount * minItemWidth;
|
|
1473
|
+
if (totalMinWidth <= this._width) {
|
|
1474
|
+
return;
|
|
1475
|
+
} else {
|
|
1476
|
+
// Prevent updates while it is already happening.
|
|
1477
|
+
this._updatingColumnsResponsive = true;
|
|
1478
|
+
|
|
1479
|
+
// Figure out how many columns to stack, and put them all in the first stack container.
|
|
1480
|
+
const finalColumnCount = Math.max(Math.floor(this._width / minItemWidth), 1);
|
|
1481
|
+
const stackColumnCount = columnCount - finalColumnCount;
|
|
1482
|
+
|
|
1483
|
+
const rootContentItem = this._groundItem.contentItems[0];
|
|
1484
|
+
const allStacks = this.getAllStacks();
|
|
1485
|
+
if (allStacks.length === 0) {
|
|
1486
|
+
throw new AssertError('LMACRS77413')
|
|
1487
|
+
} else {
|
|
1488
|
+
const firstStackContainer = allStacks[0];
|
|
1489
|
+
for (let i = 0; i < stackColumnCount; i++) {
|
|
1490
|
+
// Stack from right.
|
|
1491
|
+
const column = rootContentItem.contentItems[rootContentItem.contentItems.length - 1];
|
|
1492
|
+
this.addChildContentItemsToContainer(firstStackContainer, column);
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
this._updatingColumnsResponsive = false;
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
/**
|
|
1505
|
+
* Determines if responsive layout should be used.
|
|
1506
|
+
*
|
|
1507
|
+
* @returns True if responsive layout should be used; otherwise false.
|
|
1508
|
+
* @internal
|
|
1509
|
+
*/
|
|
1510
|
+
private useResponsiveLayout() {
|
|
1511
|
+
const settings = this.layoutConfig.settings;
|
|
1512
|
+
const alwaysResponsiveMode = settings.responsiveMode === ResponsiveMode.always;
|
|
1513
|
+
const onLoadResponsiveModeAndFirst = settings.responsiveMode === ResponsiveMode.onload && this._firstLoad;
|
|
1514
|
+
return alwaysResponsiveMode || onLoadResponsiveModeAndFirst;
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1517
|
+
/**
|
|
1518
|
+
* Adds all children of a node to another container recursively.
|
|
1519
|
+
* @param container - Container to add child content items to.
|
|
1520
|
+
* @param node - Node to search for content items.
|
|
1521
|
+
* @internal
|
|
1522
|
+
*/
|
|
1523
|
+
private addChildContentItemsToContainer(container: ContentItem, node: ContentItem) {
|
|
1524
|
+
const contentItems = node.contentItems;
|
|
1525
|
+
if (node instanceof Stack) {
|
|
1526
|
+
for (let i = 0; i < contentItems.length; i++) {
|
|
1527
|
+
const item = contentItems[i];
|
|
1528
|
+
node.removeChild(item, true);
|
|
1529
|
+
container.addChild(item);
|
|
1530
|
+
}
|
|
1531
|
+
} else {
|
|
1532
|
+
for (let i = 0; i < contentItems.length; i++) {
|
|
1533
|
+
const item = contentItems[i];
|
|
1534
|
+
this.addChildContentItemsToContainer(container, item);
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
|
|
1539
|
+
/**
|
|
1540
|
+
* Finds all the stacks.
|
|
1541
|
+
* @returns The found stack containers.
|
|
1542
|
+
* @internal
|
|
1543
|
+
*/
|
|
1544
|
+
private getAllStacks() {
|
|
1545
|
+
if (this._groundItem === undefined) {
|
|
1546
|
+
throw new UnexpectedUndefinedError('LMFASC52778');
|
|
1547
|
+
} else {
|
|
1548
|
+
const stacks: Stack[] = [];
|
|
1549
|
+
this.findAllStacksRecursive(stacks, this._groundItem);
|
|
1550
|
+
|
|
1551
|
+
return stacks;
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
/** @internal */
|
|
1556
|
+
private findFirstContentItemType(type: ItemType): ContentItem | undefined {
|
|
1557
|
+
if (this._groundItem === undefined) {
|
|
1558
|
+
throw new UnexpectedUndefinedError('LMFFCIT82446');
|
|
1559
|
+
} else {
|
|
1560
|
+
return this.findFirstContentItemTypeRecursive(type, this._groundItem);
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
/** @internal */
|
|
1565
|
+
private findFirstContentItemTypeRecursive(type: ItemType, node: ContentItem): ContentItem | undefined {
|
|
1566
|
+
const contentItems = node.contentItems;
|
|
1567
|
+
const contentItemCount = contentItems.length;
|
|
1568
|
+
if (contentItemCount === 0) {
|
|
1569
|
+
return undefined;
|
|
1570
|
+
} else {
|
|
1571
|
+
for (let i = 0; i < contentItemCount; i++) {
|
|
1572
|
+
const contentItem = contentItems[i];
|
|
1573
|
+
if (contentItem.type === type) {
|
|
1574
|
+
return contentItem;
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
for (let i = 0; i < contentItemCount; i++) {
|
|
1579
|
+
const contentItem = contentItems[i];
|
|
1580
|
+
const foundContentItem = this.findFirstContentItemTypeRecursive(type, contentItem);
|
|
1581
|
+
if (foundContentItem !== undefined) {
|
|
1582
|
+
return foundContentItem;
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
return undefined;
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
/** @internal */
|
|
1591
|
+
private findFirstContentItemTypeByIdRecursive(type: ItemType, id: string, node: ContentItem): ContentItem | undefined {
|
|
1592
|
+
const contentItems = node.contentItems;
|
|
1593
|
+
const contentItemCount = contentItems.length;
|
|
1594
|
+
if (contentItemCount === 0) {
|
|
1595
|
+
return undefined;
|
|
1596
|
+
} else {
|
|
1597
|
+
for (let i = 0; i < contentItemCount; i++) {
|
|
1598
|
+
const contentItem = contentItems[i];
|
|
1599
|
+
if (contentItem.type === type && contentItem.id === id) {
|
|
1600
|
+
return contentItem;
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
for (let i = 0; i < contentItemCount; i++) {
|
|
1605
|
+
const contentItem = contentItems[i];
|
|
1606
|
+
const foundContentItem = this.findFirstContentItemTypeByIdRecursive(type, id, contentItem);
|
|
1607
|
+
if (foundContentItem !== undefined) {
|
|
1608
|
+
return foundContentItem;
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
return undefined;
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
/**
|
|
1617
|
+
* Finds all the stack containers.
|
|
1618
|
+
*
|
|
1619
|
+
* @param stacks - Set of containers to populate.
|
|
1620
|
+
* @param node - Current node to process.
|
|
1621
|
+
* @internal
|
|
1622
|
+
*/
|
|
1623
|
+
private findAllStacksRecursive(stacks: Stack[], node: ContentItem) {
|
|
1624
|
+
const contentItems = node.contentItems;
|
|
1625
|
+
for (let i = 0; i < contentItems.length; i++) {
|
|
1626
|
+
const item = contentItems[i];
|
|
1627
|
+
if (item instanceof Stack) {
|
|
1628
|
+
stacks.push(item);
|
|
1629
|
+
} else {
|
|
1630
|
+
if (!item.isComponent) {
|
|
1631
|
+
this.findAllStacksRecursive(stacks, item);
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1637
|
+
/** @internal */
|
|
1638
|
+
private findFirstLocation(selectors: readonly LayoutManager.LocationSelector[]): LayoutManager.Location | undefined {
|
|
1639
|
+
const count = selectors.length;
|
|
1640
|
+
for (let i = 0; i < count; i++) {
|
|
1641
|
+
const selector = selectors[i];
|
|
1642
|
+
const location = this.findLocation(selector);
|
|
1643
|
+
if (location !== undefined) {
|
|
1644
|
+
return location;
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
return undefined;
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
/** @internal */
|
|
1651
|
+
private findLocation(selector: LayoutManager.LocationSelector): LayoutManager.Location | undefined {
|
|
1652
|
+
const selectorIndex = selector.index;
|
|
1653
|
+
switch (selector.typeId) {
|
|
1654
|
+
case LayoutManager.LocationSelector.TypeId.FocusedItem: {
|
|
1655
|
+
if (this._focusedComponentItem === undefined) {
|
|
1656
|
+
return undefined
|
|
1657
|
+
} else {
|
|
1658
|
+
const parentItem = this._focusedComponentItem.parentItem;
|
|
1659
|
+
const parentContentItems = parentItem.contentItems;
|
|
1660
|
+
const parentContentItemCount = parentContentItems.length;
|
|
1661
|
+
if (selectorIndex === undefined) {
|
|
1662
|
+
return { parentItem, index: parentContentItemCount };
|
|
1663
|
+
} else {
|
|
1664
|
+
const focusedIndex = parentContentItems.indexOf(this._focusedComponentItem);
|
|
1665
|
+
const index = focusedIndex + selectorIndex;
|
|
1666
|
+
if (index < 0 || index > parentContentItemCount) {
|
|
1667
|
+
return undefined;
|
|
1668
|
+
} else {
|
|
1669
|
+
return { parentItem, index };
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
case LayoutManager.LocationSelector.TypeId.FocusedStack: {
|
|
1675
|
+
if (this._focusedComponentItem === undefined) {
|
|
1676
|
+
return undefined
|
|
1677
|
+
} else {
|
|
1678
|
+
const parentItem = this._focusedComponentItem.parentItem;
|
|
1679
|
+
return this.tryCreateLocationFromParentItem(parentItem, selectorIndex);
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
case LayoutManager.LocationSelector.TypeId.FirstStack: {
|
|
1683
|
+
const parentItem = this.findFirstContentItemType(ItemType.stack);
|
|
1684
|
+
if (parentItem === undefined) {
|
|
1685
|
+
return undefined;
|
|
1686
|
+
} else {
|
|
1687
|
+
return this.tryCreateLocationFromParentItem(parentItem, selectorIndex);
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
case LayoutManager.LocationSelector.TypeId.FirstRowOrColumn: {
|
|
1691
|
+
let parentItem = this.findFirstContentItemType(ItemType.row);
|
|
1692
|
+
if (parentItem !== undefined) {
|
|
1693
|
+
return this.tryCreateLocationFromParentItem(parentItem, selectorIndex);
|
|
1694
|
+
} else {
|
|
1695
|
+
parentItem = this.findFirstContentItemType(ItemType.column);
|
|
1696
|
+
if (parentItem !== undefined) {
|
|
1697
|
+
return this.tryCreateLocationFromParentItem(parentItem, selectorIndex);
|
|
1698
|
+
} else {
|
|
1699
|
+
return undefined;
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
case LayoutManager.LocationSelector.TypeId.FirstRow: {
|
|
1704
|
+
const parentItem = this.findFirstContentItemType(ItemType.row);
|
|
1705
|
+
if (parentItem === undefined) {
|
|
1706
|
+
return undefined;
|
|
1707
|
+
} else {
|
|
1708
|
+
return this.tryCreateLocationFromParentItem(parentItem, selectorIndex);
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
case LayoutManager.LocationSelector.TypeId.FirstColumn: {
|
|
1712
|
+
const parentItem = this.findFirstContentItemType(ItemType.column);
|
|
1713
|
+
if (parentItem === undefined) {
|
|
1714
|
+
return undefined;
|
|
1715
|
+
} else {
|
|
1716
|
+
return this.tryCreateLocationFromParentItem(parentItem, selectorIndex);
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
case LayoutManager.LocationSelector.TypeId.Empty: {
|
|
1720
|
+
if (this._groundItem === undefined) {
|
|
1721
|
+
throw new UnexpectedUndefinedError('LMFLRIF18244');
|
|
1722
|
+
} else {
|
|
1723
|
+
if (this.rootItem !== undefined) {
|
|
1724
|
+
return undefined;
|
|
1725
|
+
} else {
|
|
1726
|
+
if (selectorIndex === undefined || selectorIndex === 0)
|
|
1727
|
+
return { parentItem: this._groundItem, index: 0 };
|
|
1728
|
+
else {
|
|
1729
|
+
return undefined;
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
case LayoutManager.LocationSelector.TypeId.Root: {
|
|
1735
|
+
if (this._groundItem === undefined) {
|
|
1736
|
+
throw new UnexpectedUndefinedError('LMFLF18244');
|
|
1737
|
+
} else {
|
|
1738
|
+
const groundContentItems = this._groundItem.contentItems;
|
|
1739
|
+
if (groundContentItems.length === 0) {
|
|
1740
|
+
if (selectorIndex === undefined || selectorIndex === 0)
|
|
1741
|
+
return { parentItem: this._groundItem, index: 0 };
|
|
1742
|
+
else {
|
|
1743
|
+
return undefined;
|
|
1744
|
+
}
|
|
1745
|
+
} else {
|
|
1746
|
+
const parentItem = groundContentItems[0];
|
|
1747
|
+
return this.tryCreateLocationFromParentItem(parentItem, selectorIndex);
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
|
|
1754
|
+
/** @internal */
|
|
1755
|
+
private tryCreateLocationFromParentItem(parentItem: ContentItem,
|
|
1756
|
+
selectorIndex: number | undefined
|
|
1757
|
+
): LayoutManager.Location | undefined {
|
|
1758
|
+
const parentContentItems = parentItem.contentItems;
|
|
1759
|
+
const parentContentItemCount = parentContentItems.length;
|
|
1760
|
+
if (selectorIndex === undefined) {
|
|
1761
|
+
return { parentItem, index: parentContentItemCount };
|
|
1762
|
+
} else {
|
|
1763
|
+
if (selectorIndex < 0 || selectorIndex > parentContentItemCount) {
|
|
1764
|
+
return undefined;
|
|
1765
|
+
} else {
|
|
1766
|
+
return { parentItem, index: selectorIndex };
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1772
|
+
/** @public */
|
|
1773
|
+
export namespace LayoutManager {
|
|
1774
|
+
export type BeforeVirtualRectingEvent = (this: void, count: number) => void;
|
|
1775
|
+
export type AfterVirtualRectingEvent = (this: void) => void;
|
|
1776
|
+
|
|
1777
|
+
/** @internal */
|
|
1778
|
+
export interface ConstructorParameters {
|
|
1779
|
+
constructorOrSubWindowLayoutConfig: LayoutConfig | undefined;
|
|
1780
|
+
isSubWindow: boolean;
|
|
1781
|
+
containerElement: HTMLElement | undefined;
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1784
|
+
/** @internal */
|
|
1785
|
+
export function createMaximisePlaceElement(document: Document): HTMLElement {
|
|
1786
|
+
const element = document.createElement('div');
|
|
1787
|
+
element.classList.add(DomConstants.ClassName.MaximisePlace);
|
|
1788
|
+
return element;
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1791
|
+
/** @internal */
|
|
1792
|
+
export function createTabDropPlaceholderElement(document: Document): HTMLElement {
|
|
1793
|
+
const element = document.createElement('div');
|
|
1794
|
+
element.classList.add(DomConstants.ClassName.DropTabPlaceholder);
|
|
1795
|
+
return element;
|
|
1796
|
+
}
|
|
1797
|
+
|
|
1798
|
+
/**
|
|
1799
|
+
* Specifies a location of a ContentItem without referencing the content item.
|
|
1800
|
+
* Used to specify where a new item is to be added
|
|
1801
|
+
* @public
|
|
1802
|
+
*/
|
|
1803
|
+
export interface Location {
|
|
1804
|
+
parentItem: ContentItem;
|
|
1805
|
+
index: number;
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
/**
|
|
1809
|
+
* A selector used to specify a unique location in the layout
|
|
1810
|
+
* @public
|
|
1811
|
+
*/
|
|
1812
|
+
export interface LocationSelector {
|
|
1813
|
+
/** Specifies selector algorithm */
|
|
1814
|
+
typeId: LocationSelector.TypeId;
|
|
1815
|
+
/** Used by algorithm to determine index in found ContentItem */
|
|
1816
|
+
index?: number;
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1819
|
+
/** @public */
|
|
1820
|
+
export namespace LocationSelector {
|
|
1821
|
+
export const enum TypeId {
|
|
1822
|
+
/** Stack with focused Item. Index specifies offset from index of focused item (eg 1 is the position after focused item) */
|
|
1823
|
+
FocusedItem,
|
|
1824
|
+
/** Stack with focused Item. Index specfies ContentItems index */
|
|
1825
|
+
FocusedStack,
|
|
1826
|
+
/** First stack found in layout */
|
|
1827
|
+
FirstStack,
|
|
1828
|
+
/** First Row or Column found in layout (rows are searched first) */
|
|
1829
|
+
FirstRowOrColumn,
|
|
1830
|
+
/** First Row in layout */
|
|
1831
|
+
FirstRow,
|
|
1832
|
+
/** First Column in layout */
|
|
1833
|
+
FirstColumn,
|
|
1834
|
+
/** Finds a location if layout is empty. The found location will be the root ContentItem. */
|
|
1835
|
+
Empty,
|
|
1836
|
+
/** Finds root if layout is empty, otherwise a child under root */
|
|
1837
|
+
Root,
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
|
|
1841
|
+
/**
|
|
1842
|
+
* Default LocationSelectors array used if none is specified. Will always find a location.
|
|
1843
|
+
* @public
|
|
1844
|
+
*/
|
|
1845
|
+
export const defaultLocationSelectors: readonly LocationSelector[] = [
|
|
1846
|
+
{ typeId: LocationSelector.TypeId.FocusedStack, index: undefined },
|
|
1847
|
+
{ typeId: LocationSelector.TypeId.FirstStack, index: undefined },
|
|
1848
|
+
{ typeId: LocationSelector.TypeId.FirstRowOrColumn, index: undefined },
|
|
1849
|
+
{ typeId: LocationSelector.TypeId.Root, index: undefined },
|
|
1850
|
+
];
|
|
1851
|
+
|
|
1852
|
+
/**
|
|
1853
|
+
* LocationSelectors to try to get location next to existing focused item
|
|
1854
|
+
* @public
|
|
1855
|
+
*/
|
|
1856
|
+
export const afterFocusedItemIfPossibleLocationSelectors: readonly LocationSelector[] = [
|
|
1857
|
+
{ typeId: LocationSelector.TypeId.FocusedItem, index: 1 },
|
|
1858
|
+
{ typeId: LocationSelector.TypeId.FirstStack, index: undefined },
|
|
1859
|
+
{ typeId: LocationSelector.TypeId.FirstRowOrColumn, index: undefined },
|
|
1860
|
+
{ typeId: LocationSelector.TypeId.Root, index: undefined },
|
|
1861
|
+
];
|
|
1862
|
+
}
|