golden-layout
Version:
A multi-screen javascript Layout manager
295 lines • 14.3 kB
JavaScript
import { ResolvedComponentItemConfig } from './config/resolved-config';
import { ApiError, BindError } from './errors/external-error';
import { AssertError, UnexpectedUndefinedError } from './errors/internal-error';
import { i18nStrings } from './utils/i18n-strings';
import { deepExtendValue, ensureElementPositionAbsolute, numberToPixels, setElementDisplayVisibility, setElementHeight, setElementWidth } from './utils/utils';
import { VirtualLayout } from './virtual-layout';
/** @public */
export class GoldenLayout extends VirtualLayout {
/** @internal */
constructor(configOrOptionalContainer, containerOrBindComponentEventHandler, unbindComponentEventHandler) {
super(configOrOptionalContainer, containerOrBindComponentEventHandler, unbindComponentEventHandler, true);
/** @internal */
this._componentTypesMap = new Map();
/** @internal */
this._registeredComponentMap = new Map();
/** @internal */
this._virtuableComponentMap = new Map();
/** @internal */
this._containerVirtualRectingRequiredEventListener = (container, width, height) => this.handleContainerVirtualRectingRequiredEvent(container, width, height);
/** @internal */
this._containerVirtualVisibilityChangeRequiredEventListener = (container, visible) => this.handleContainerVirtualVisibilityChangeRequiredEvent(container, visible);
/** @internal */
this._containerVirtualZIndexChangeRequiredEventListener = (container, logicalZIndex, defaultZIndex) => this.handleContainerVirtualZIndexChangeRequiredEvent(container, logicalZIndex, defaultZIndex);
// we told VirtualLayout to not call init() (skipInit set to true) so that Golden Layout can initialise its properties before init is called
if (!this.deprecatedConstructor) {
this.init();
}
}
/**
* Register a new component type with the layout manager.
*
* @deprecated See {@link https://stackoverflow.com/questions/40922531/how-to-check-if-a-javascript-function-is-a-constructor}
* instead use {@link (GoldenLayout:class).registerComponentConstructor}
* or {@link (GoldenLayout:class).registerComponentFactoryFunction}
*/
registerComponent(name, componentConstructorOrFactoryFtn, virtual = false) {
if (typeof componentConstructorOrFactoryFtn !== 'function') {
throw new ApiError('registerComponent() componentConstructorOrFactoryFtn parameter is not a function');
}
else {
if (componentConstructorOrFactoryFtn.hasOwnProperty('prototype')) {
const componentConstructor = componentConstructorOrFactoryFtn;
this.registerComponentConstructor(name, componentConstructor, virtual);
}
else {
const componentFactoryFtn = componentConstructorOrFactoryFtn;
this.registerComponentFactoryFunction(name, componentFactoryFtn, virtual);
}
}
}
/**
* Register a new component type with the layout manager.
*/
registerComponentConstructor(typeName, componentConstructor, virtual = false) {
if (typeof componentConstructor !== 'function') {
throw new Error(i18nStrings[1 /* PleaseRegisterAConstructorFunction */]);
}
const existingComponentType = this._componentTypesMap.get(typeName);
if (existingComponentType !== undefined) {
throw new BindError(`${i18nStrings[3 /* ComponentIsAlreadyRegistered */]}: ${typeName}`);
}
this._componentTypesMap.set(typeName, {
constructor: componentConstructor,
factoryFunction: undefined,
virtual,
});
}
/**
* Register a new component with the layout manager.
*/
registerComponentFactoryFunction(typeName, componentFactoryFunction, virtual = false) {
if (typeof componentFactoryFunction !== 'function') {
throw new BindError('Please register a constructor function');
}
const existingComponentType = this._componentTypesMap.get(typeName);
if (existingComponentType !== undefined) {
throw new BindError(`${i18nStrings[3 /* ComponentIsAlreadyRegistered */]}: ${typeName}`);
}
this._componentTypesMap.set(typeName, {
constructor: undefined,
factoryFunction: componentFactoryFunction,
virtual,
});
}
/**
* Register a component function with the layout manager. This function should
* return a constructor for a component based on a config.
* This function will be called if a component type with the required name is not already registered.
* It is recommended that applications use the {@link (VirtualLayout:class).getComponentEvent} and
* {@link (VirtualLayout:class).releaseComponentEvent} instead of registering a constructor callback
* @deprecated use {@link (GoldenLayout:class).registerGetComponentConstructorCallback}
*/
registerComponentFunction(callback) {
this.registerGetComponentConstructorCallback(callback);
}
/**
* Register a callback closure with the layout manager which supplies a Component Constructor.
* This callback should return a constructor for a component based on a config.
* This function will be called if a component type with the required name is not already registered.
* It is recommended that applications use the {@link (VirtualLayout:class).getComponentEvent} and
* {@link (VirtualLayout:class).releaseComponentEvent} instead of registering a constructor callback
*/
registerGetComponentConstructorCallback(callback) {
if (typeof callback !== 'function') {
throw new Error('Please register a callback function');
}
if (this._getComponentConstructorFtn !== undefined) {
console.warn('Multiple component functions are being registered. Only the final registered function will be used.');
}
this._getComponentConstructorFtn = callback;
}
getRegisteredComponentTypeNames() {
const typeNamesIterableIterator = this._componentTypesMap.keys();
return Array.from(typeNamesIterableIterator);
}
/**
* Returns a previously registered component instantiator. Attempts to utilize registered
* component type by first, then falls back to the component constructor callback function (if registered).
* If neither gets an instantiator, then returns `undefined`.
* Note that `undefined` will return if config.componentType is not a string
*
* @param config - The item config
* @public
*/
getComponentInstantiator(config) {
let instantiator;
const typeName = ResolvedComponentItemConfig.resolveComponentTypeName(config);
if (typeName !== undefined) {
instantiator = this._componentTypesMap.get(typeName);
}
if (instantiator === undefined) {
if (this._getComponentConstructorFtn !== undefined) {
instantiator = {
constructor: this._getComponentConstructorFtn(config),
factoryFunction: undefined,
virtual: false,
};
}
}
return instantiator;
}
/** @internal */
bindComponent(container, itemConfig) {
let instantiator;
const typeName = ResolvedComponentItemConfig.resolveComponentTypeName(itemConfig);
if (typeName !== undefined) {
instantiator = this._componentTypesMap.get(typeName);
}
if (instantiator === undefined) {
if (this._getComponentConstructorFtn !== undefined) {
instantiator = {
constructor: this._getComponentConstructorFtn(itemConfig),
factoryFunction: undefined,
virtual: false,
};
}
}
let result;
if (instantiator !== undefined) {
const virtual = instantiator.virtual;
// handle case where component is obtained by name or component constructor callback
let componentState;
if (itemConfig.componentState === undefined) {
componentState = undefined;
}
else {
// make copy
componentState = deepExtendValue({}, itemConfig.componentState);
}
let component;
const componentConstructor = instantiator.constructor;
if (componentConstructor !== undefined) {
component = new componentConstructor(container, componentState, virtual);
}
else {
const factoryFunction = instantiator.factoryFunction;
if (factoryFunction !== undefined) {
component = factoryFunction(container, componentState, virtual);
}
else {
throw new AssertError('LMBCFFU10008');
}
}
if (virtual) {
if (component === undefined) {
throw new UnexpectedUndefinedError('GLBCVCU988774');
}
else {
const virtuableComponent = component;
const componentRootElement = virtuableComponent.rootHtmlElement;
if (componentRootElement === undefined) {
throw new BindError(`${i18nStrings[5 /* VirtualComponentDoesNotHaveRootHtmlElement */]}: ${typeName}`);
}
else {
ensureElementPositionAbsolute(componentRootElement);
this.container.appendChild(componentRootElement);
this._virtuableComponentMap.set(container, virtuableComponent);
container.virtualRectingRequiredEvent = this._containerVirtualRectingRequiredEventListener;
container.virtualVisibilityChangeRequiredEvent = this._containerVirtualVisibilityChangeRequiredEventListener;
container.virtualZIndexChangeRequiredEvent = this._containerVirtualZIndexChangeRequiredEventListener;
}
}
}
this._registeredComponentMap.set(container, component);
result = {
virtual: instantiator.virtual,
component,
};
}
else {
// Use getComponentEvent
result = super.bindComponent(container, itemConfig);
}
return result;
}
/** @internal */
unbindComponent(container, virtual, component) {
const registeredComponent = this._registeredComponentMap.get(container);
if (registeredComponent === undefined) {
super.unbindComponent(container, virtual, component); // was not created from registration so use virtual unbind events
}
else {
const virtuableComponent = this._virtuableComponentMap.get(container);
if (virtuableComponent !== undefined) {
const componentRootElement = virtuableComponent.rootHtmlElement;
if (componentRootElement === undefined) {
throw new AssertError('GLUC77743', container.title);
}
else {
this.container.removeChild(componentRootElement);
this._virtuableComponentMap.delete(container);
}
}
}
}
fireBeforeVirtualRectingEvent(count) {
this._goldenLayoutBoundingClientRect = this.container.getBoundingClientRect();
super.fireBeforeVirtualRectingEvent(count);
}
/** @internal */
handleContainerVirtualRectingRequiredEvent(container, width, height) {
const virtuableComponent = this._virtuableComponentMap.get(container);
if (virtuableComponent === undefined) {
throw new UnexpectedUndefinedError('GLHCSCE55933');
}
else {
const rootElement = virtuableComponent.rootHtmlElement;
if (rootElement === undefined) {
throw new BindError(i18nStrings[4 /* ComponentIsNotVirtuable */] + ' ' + container.title);
}
else {
const containerBoundingClientRect = container.element.getBoundingClientRect();
const left = containerBoundingClientRect.left - this._goldenLayoutBoundingClientRect.left;
rootElement.style.left = numberToPixels(left);
const top = containerBoundingClientRect.top - this._goldenLayoutBoundingClientRect.top;
rootElement.style.top = numberToPixels(top);
setElementWidth(rootElement, width);
setElementHeight(rootElement, height);
}
}
}
/** @internal */
handleContainerVirtualVisibilityChangeRequiredEvent(container, visible) {
const virtuableComponent = this._virtuableComponentMap.get(container);
if (virtuableComponent === undefined) {
throw new UnexpectedUndefinedError('GLHCVVCRE55934');
}
else {
const rootElement = virtuableComponent.rootHtmlElement;
if (rootElement === undefined) {
throw new BindError(i18nStrings[4 /* ComponentIsNotVirtuable */] + ' ' + container.title);
}
else {
setElementDisplayVisibility(rootElement, visible);
}
}
}
/** @internal */
handleContainerVirtualZIndexChangeRequiredEvent(container, logicalZIndex, defaultZIndex) {
const virtuableComponent = this._virtuableComponentMap.get(container);
if (virtuableComponent === undefined) {
throw new UnexpectedUndefinedError('GLHCVZICRE55935');
}
else {
const rootElement = virtuableComponent.rootHtmlElement;
if (rootElement === undefined) {
throw new BindError(i18nStrings[4 /* ComponentIsNotVirtuable */] + ' ' + container.title);
}
else {
rootElement.style.zIndex = defaultZIndex;
}
}
}
}
//# sourceMappingURL=golden-layout.js.map