UNPKG

@devexperts/dxcharts-lite

Version:
475 lines (474 loc) 28.5 kB
/* * Copyright (C) 2019 - 2025 Devexperts Solutions IE Limited * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. * If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import { merge as mergeRx } from 'rxjs'; import { CanvasAnimation } from './animation/canvas-animation'; import { CHART_UUID, CanvasBoundsContainer, CanvasElement } from './canvas/canvas-bounds-container'; import { CursorHandler } from './canvas/cursor.handler'; import { createDefaultLayoutTemplate, extractElements } from './canvas/layout-creator'; import { mergeWithDefaultConfig, } from './chart.config'; import { ChartBaseModel } from './components/chart/chart-base.model'; import { ChartComponent } from './components/chart/chart.component'; import { ChartModel } from './components/chart/chart.model'; import { CrossToolComponent } from './components/cross_tool/cross-tool.component'; import { DynamicObjectsComponent } from './components/dynamic-objects/dynamic-objects.component'; import { EventsComponent } from './components/events/events.component'; import { GridComponent } from './components/grid/grid.component'; import { HighLowComponent } from './components/high_low/high-low.component'; import { HighlightsComponent } from './components/highlights/highlights.component'; import { NavigationMapComponent } from './components/navigation_map/navigation-map.component'; import { ChartPanComponent } from './components/pan/chart-pan.component'; import { PaneManager } from './components/pane/pane-manager.component'; import { SnapshotComponent } from './components/snapshot/snapshot.component'; import { VolumesComponent } from './components/volumes/volumes.component'; import { WaterMarkComponent } from './components/watermark/water-mark.component'; import { XAxisComponent } from './components/x_axis/x-axis.component'; import { LastCandleLabelsProvider } from './components/y_axis/price_labels/last-candle-labels.provider'; import { LabelsGroups } from './components/y_axis/price_labels/y-axis-labels.model'; import { YAxisPriceLabelsDrawer } from './components/y_axis/price_labels/y-axis-price-labels.drawer'; import { YAxisDrawer } from './components/y_axis/y-axis.drawer'; import { ClearCanvasDrawer } from './drawers/clear-canvas.drawer'; import { CompositeDrawer } from './drawers/composite.drawer'; import { DrawingManager } from './drawers/drawing-manager'; import EventBus from './events/event-bus'; import { CandleTapHandler } from './inputhandlers/candle-tap.handler'; import { ChartResizeHandler } from './inputhandlers/chart-resize.handler'; import { CrossEventProducerComponent } from './inputhandlers/cross-event-producer.component'; import { HoverProducerComponent } from './inputhandlers/hover-producer.component'; import { CanvasInputListenerComponent } from './inputlisteners/canvas-input-listener.component'; import { createCanvasModel, createMainCanvasModel } from './model/canvas.model'; import { HitTestCanvasModel } from './model/hit-test-canvas.model'; import { ScaleModel } from './model/scale.model'; import { TimeZoneModel } from './model/time-zone.model'; import { clearerSafe } from './utils/function.utils'; import { merge } from './utils/merge.utils'; import { HitTestComponent } from './components/hit-test/hit-test.component'; import { isSafari } from './utils/device/touchpad.utils'; import { SafariCanvasAnimation } from './utils/performance/safari/components/animations/safari-canvas-animation'; /** * @deprecated use {Chart} instead */ export default class ChartBootstrap { constructor(element, userConfig = {}) { var _a, _b; this.components = []; this.chartComponents = []; // components list which listen for mouse and keyboard this.userInputListenerComponents = []; this.canvasModels = []; this.parentElement = element; // eslint-disable-next-line no-restricted-syntax const config = userConfig; mergeWithDefaultConfig(config); this.config = config; const chartLayoutTemplate = createDefaultLayoutTemplate(config); element.innerHTML = ''; element.appendChild(chartLayoutTemplate.content); this.id = (_a = element.getAttribute('data-id')) !== null && _a !== void 0 ? _a : ''; if (config.fixedSize) { element.style.width = config.fixedSize.width + 'px'; element.style.height = config.fixedSize.height + 'px'; } const timeZoneModel = new TimeZoneModel(config); this.timeZoneModel = timeZoneModel; const formatterFactory = this.timeZoneModel.getFormatterFactory(); const elements = extractElements(element); this.elements = elements; const eventBus = new EventBus(); this.bus = eventBus; const chartResizeHandler = new ChartResizeHandler(element, (_b = elements.chartResizer) !== null && _b !== void 0 ? _b : element, eventBus, this.canvasModels, config); this.chartResizeHandler = chartResizeHandler; chartResizeHandler.subscribeResize(); this.components.push(chartResizeHandler.unsubscribeAnimationUpdate.bind(chartResizeHandler)); const drawingManager = new DrawingManager(eventBus, chartResizeHandler); this.drawingManager = drawingManager; const mainCanvasModel = createMainCanvasModel(eventBus, elements.mainCanvas, elements.chartResizer, this.config.components.chart.type, this.config, drawingManager, this.canvasModels); this.mainCanvasModel = mainCanvasModel; this.dynamicObjectsCanvasModel = createCanvasModel(eventBus, elements.dynamicObjectsCanvas, config, drawingManager, this.canvasModels, elements.chartResizer); const dataSeriesCanvasClearDrawer = new ClearCanvasDrawer(this.dynamicObjectsCanvasModel); drawingManager.addDrawer(dataSeriesCanvasClearDrawer, 'SERIES_CLEAR'); const yAxisLabelsCanvasModel = createCanvasModel(eventBus, elements.yAxisLabelsCanvas, config, drawingManager, this.canvasModels, elements.chartResizer); const yAxisDescriptionsCanvasModel = createCanvasModel(eventBus, elements.yAxisDescriptionsCanvas, config, drawingManager, this.canvasModels, elements.chartResizer); const canvasBoundsContainer = new CanvasBoundsContainer(config, eventBus, mainCanvasModel, formatterFactory, chartResizeHandler); this.canvasBoundsContainer = canvasBoundsContainer; //#region main canvas user input listeners const mainCanvasParent = elements.mainCanvas.parentElement; if (mainCanvasParent === null) { throw new Error(`Couldn't get main canvas parent`); } const canvasInputListener = new CanvasInputListenerComponent(eventBus, mainCanvasParent); this.canvasInputListener = canvasInputListener; this.chartComponents.push(this.canvasInputListener); //#endregion const hitTestCanvasModelParams = [ eventBus, elements.hitTestCanvas, canvasInputListener, canvasBoundsContainer, drawingManager, config, this.canvasModels, elements.chartResizer, ]; const hitTestCanvasModel = new HitTestCanvasModel(...hitTestCanvasModelParams); this.hitTestCanvasModel = hitTestCanvasModel; const hitTestClearCanvas = new ClearCanvasDrawer(hitTestCanvasModel); drawingManager.addDrawer(hitTestClearCanvas, 'HIT_TEST_CLEAR'); //#endregion // canvas animation container, safari has separate animation logic due to browser specifics const canvasAnimation = isSafari ? new SafariCanvasAnimation(eventBus) : new CanvasAnimation(eventBus); this.canvasAnimation = canvasAnimation; const chartPaneId = CanvasElement.PANE_UUID(CHART_UUID); //#region ScaleModel init const scaleModel = new ScaleModel(config, () => canvasBoundsContainer.getBounds(chartPaneId), canvasAnimation); this.scaleModel = scaleModel; //#endregion const backgroundCanvasModel = createCanvasModel(eventBus, elements.backgroundCanvas, config, drawingManager, this.canvasModels, elements.chartResizer, { // can be read frequently, see {redrawBackgroundArea} function willReadFrequently: true, }); this.backgroundCanvasModel = backgroundCanvasModel; this.cursorHandler = new CursorHandler(elements.canvasArea, canvasInputListener, canvasBoundsContainer, hitTestCanvasModel); this.chartComponents.push(this.cursorHandler); this.crossEventProducer = new CrossEventProducerComponent(canvasInputListener, canvasBoundsContainer); this.chartComponents.push(this.crossEventProducer); const chartBaseModel = new ChartBaseModel('candle'); this.chartBaseModel = chartBaseModel; const chartPanComponent = new ChartPanComponent(eventBus, scaleModel, canvasBoundsContainer, config, canvasAnimation, canvasInputListener, mainCanvasParent, chartBaseModel, hitTestCanvasModel); this.chartPanComponent = chartPanComponent; this.chartComponents.push(chartPanComponent); this.userInputListenerComponents.push(chartPanComponent.chartAreaPanHandler); const hitTestComponent = new HitTestComponent(hitTestCanvasModel, canvasAnimation, eventBus); this.hitTestComponent = hitTestComponent; this.chartComponents.push(hitTestComponent); const paneManager = new PaneManager(chartBaseModel, this.dynamicObjectsCanvasModel, this.userInputListenerComponents, eventBus, scaleModel, canvasBoundsContainer, config, canvasAnimation, canvasInputListener, drawingManager, this.cursorHandler, this.crossEventProducer, chartPanComponent, mainCanvasModel, yAxisLabelsCanvasModel, this.hitTestCanvasModel, this.chartResizeHandler); this.paneManager = paneManager; const mainPane = this.paneManager.panes[CHART_UUID]; this.mainPane = mainPane; this.chartComponents.push(paneManager); // dynamic objects component this.dynamicObjects = new DynamicObjectsComponent(this.dynamicObjectsCanvasModel, drawingManager); this.chartComponents.push(this.dynamicObjects); this.chartModel = new ChartModel(chartBaseModel, paneManager, eventBus, this.dynamicObjectsCanvasModel, config, scaleModel, formatterFactory, mainCanvasParent, canvasBoundsContainer, chartResizeHandler); //#region main chart component init const chartComponent = new ChartComponent(this.chartModel, this.dynamicObjectsCanvasModel, config, scaleModel, canvasBoundsContainer, drawingManager, hitTestCanvasModel, canvasInputListener, backgroundCanvasModel, chartPanComponent, paneManager, this.cursorHandler, this.dynamicObjects, this.chartResizeHandler); this.chartComponents.push(chartComponent); this.chartComponent = chartComponent; const chartModel = this.chartComponent.chartModel; this.chartModel = chartModel; this.canvasBoundsContainer.setMainCandleSeries(this.chartModel.mainCandleSeries); hitTestCanvasModel.addSubscriber(paneManager.hitTestController); // X-axis component this.xAxisComponent = new XAxisComponent(eventBus, config, mainCanvasModel, chartComponent, scaleModel, canvasBoundsContainer, canvasInputListener, chartResizeHandler, this.drawingManager, timeZoneModel, chartPanComponent, this.cursorHandler, this.hitTestCanvasModel); this.chartComponents.push(this.xAxisComponent); this.userInputListenerComponents.push(this.xAxisComponent.xAxisScaleHandler); const mainCanvasClearDrawer = new ClearCanvasDrawer(mainCanvasModel); drawingManager.addDrawer(mainCanvasClearDrawer, 'MAIN_CLEAR'); const candleTap = new CandleTapHandler(mainCanvasParent, chartModel, canvasInputListener, config); this.chartComponents.push(candleTap); // watermark component this.watermarkComponent = new WaterMarkComponent(paneManager, chartModel, eventBus, config, canvasBoundsContainer, this.dynamicObjectsCanvasModel, drawingManager); this.chartComponents.push(this.watermarkComponent); const crossToolCanvasModel = createCanvasModel(eventBus, elements.crossToolCanvas, config, drawingManager, this.canvasModels, elements.chartResizer); this.highlightsComponent = new HighlightsComponent(eventBus, config, chartModel, mainCanvasModel, canvasBoundsContainer, drawingManager); this.chartComponents.push(this.highlightsComponent); // setup default rules for UTC datetime override if (config.useUTCTimeOverride && config.dateFormatter && !config.dateFormatter.utcTimeOverride) { config.dateFormatter.utcTimeOverride = this.createUTCTimeOverrideConfig(chartModel); } // X navigation area component this.navigationMapComponent = new NavigationMapComponent(eventBus, chartModel, mainCanvasModel, config, canvasInputListener, canvasBoundsContainer, drawingManager, formatterFactory, chartPanComponent, this.cursorHandler); this.chartComponents.push(this.navigationMapComponent); this.userInputListenerComponents.push(this.navigationMapComponent.navigationMapMoveHandler); // high low component const highLowComponent = new HighLowComponent(config, this.dynamicObjectsCanvasModel, chartModel, canvasBoundsContainer, drawingManager); this.chartComponents.push(highLowComponent); this.initYAxisDrawer(yAxisLabelsCanvasModel, yAxisDescriptionsCanvasModel); this.yAxisComponent = mainPane.mainExtent.yAxis; // default labels provider const lastCandleLabelsProvider = new LastCandleLabelsProvider(this.chartModel, this.config, mainPane.mainExtent.yAxis.state, this.chartModel.lastCandleLabelsByChartType, this.yAxisComponent.getLabelsColorResolver.bind(this.yAxisComponent)); this.yAxisComponent.registerYAxisLabelsProvider(lastCandleLabelsProvider, LabelsGroups.MAIN); this.volumesComponent = new VolumesComponent(this.dynamicObjectsCanvasModel, chartComponent, scaleModel, canvasBoundsContainer, drawingManager, config, paneManager, this.dynamicObjects); this.chartComponents.push(this.volumesComponent); // grid component const verticalGridComponent = new GridComponent(mainCanvasModel, scaleModel, config, this.yAxisComponent.state, 'GRID', drawingManager, () => this.canvasBoundsContainer.getBounds(CanvasElement.ALL_PANES), () => this.canvasBoundsContainer.getBounds(chartPaneId), () => this.xAxisComponent.xAxisLabelsGenerator.labels, () => [], undefined, undefined, () => config.components.grid.visible, this.mainPane.mainExtent.idx); this.chartComponents.push(verticalGridComponent); this.hoverProducer = new HoverProducerComponent(this.crossEventProducer, scaleModel, config, chartModel, canvasInputListener, this.canvasBoundsContainer, this.paneManager, timeZoneModel, chartPanComponent.mainCanvasTouchHandler, formatterFactory); this.chartComponents.push(this.hoverProducer); this.crossToolComponent = new CrossToolComponent(config, crossToolCanvasModel, canvasBoundsContainer, drawingManager, paneManager, this.crossEventProducer, this.hoverProducer, this.chartComponent.baselineModel, this.chartModel); this.chartComponents.push(this.crossToolComponent); // Snapshot component const snapshotCanvasModel = createCanvasModel(eventBus, elements.snapshotCanvas, config, drawingManager, this.canvasModels, elements.chartResizer); const snapshotComponent = new SnapshotComponent(this.elements, snapshotCanvasModel); this.snapshotComponent = snapshotComponent; this.chartComponents.push(snapshotComponent); // events const eventsComponent = new EventsComponent(config, this.dynamicObjectsCanvasModel, hitTestCanvasModel, chartModel, canvasBoundsContainer, drawingManager, formatterFactory, this.cursorHandler); this.eventsComponent = eventsComponent; this.chartComponents.push(eventsComponent); // Temporary solution - all in one place this.chartComponents.forEach(c => c.activate()); this.enableUserControls(); drawingManager.reorderDrawers(config.drawingOrder); this.clearer = clearerSafe(this.components); } initYAxisDrawer(yAxisLabelsCanvasModel, yAxisDescriptionsCanvasModel) { const yAxisCompositeDrawer = new CompositeDrawer(); const clearYAxis = new ClearCanvasDrawer(yAxisLabelsCanvasModel); const clearYAxisDescriptions = new ClearCanvasDrawer(yAxisDescriptionsCanvasModel); yAxisCompositeDrawer.addDrawer(clearYAxis, 'YAXIS_CLEAR'); yAxisCompositeDrawer.addDrawer(clearYAxisDescriptions, 'Y_AXIS_DESCRIPTIONS_CLEAR'); this.drawingManager.addDrawer(yAxisCompositeDrawer, 'Y_AXIS'); const yAxisDrawer = new YAxisDrawer(this.config, yAxisLabelsCanvasModel, this.paneManager); yAxisCompositeDrawer.addDrawer(yAxisDrawer); const yAxisPriceLabelsDrawer = new YAxisPriceLabelsDrawer(yAxisLabelsCanvasModel, yAxisDescriptionsCanvasModel, this.backgroundCanvasModel, this.canvasBoundsContainer, this.config, this.paneManager); this.drawingManager.addDrawer(yAxisPriceLabelsDrawer, 'Y_AXIS_PRICE_LABELS'); } // TODO remove chartModel dependency, put period to global config somewhere /** * Creates a configuration object for overriding UTC time in a chart. * @param {ChartModel} chartModel - The chart model object. * @returns {Object} - The configuration object containing a pattern and a test function. * The pattern is a string representing the date format to be used for the override. * The test function checks if the pattern contains hours/minutes/seconds and if the current chart period is more than 1 day. * If both conditions are met, the function returns true, indicating that the override should be applied. * Note: The chartModel dependency should be removed and the period should be put in a global config somewhere. */ createUTCTimeOverrideConfig(chartModel) { // default rules for UTC datetime override - if datetime pattern contains hours/minutes/seconds and current // chart period is more than 1d we override it with simple UTC date without any specific timezones applied const re = new RegExp('HH|H|mm|m|s|ss|sss|SSS'); return { pattern: 'MM/dd/YY', test: (pattern) => re.test(pattern) && (chartModel.getPeriod() || 0) >= 86400, }; } /** * Merges a local configuration object with a global configuration object recursively. * @template L - Type of the local configuration object. * @param {L} local - The local configuration object to be merged. * @param {Record<string, any>} global - The global configuration object to be merged. * @returns {L} - The merged local configuration object. */ static mergeConfig(local, global) { for (const k in global) { if (k in local) { if (typeof local[k] === 'object') { ChartBootstrap.mergeConfig(local[k], global[k]); } } else { // @ts-ignore local[k] = global[k]; } } return local; } /** * Returns the FullChartConfig object. * * @returns {FullChartConfig} The FullChartConfig object. */ getConfig() { // eslint-disable-next-line no-restricted-syntax return this.config; } /** * Disables user controls by deactivating all userInputListenerComponents and disabling hitTestCanvasModel. * @returns {void} */ disableUserControls() { this.userInputListenerComponents.forEach(component => component.deactivate()); this.hitTestCanvasModel.disableUserControls(); } /** * Enables user controls for the hit test canvas model and all the user input listener components. * @returns {void} */ enableUserControls() { this.userInputListenerComponents.forEach(component => component.activate()); this.hitTestCanvasModel.enableUserControls(); } /** * Sets the configuration for the grid component of the chart. * @param {GridComponentConfig} config - The configuration object for the grid component. * @returns {void} */ setGridConfig(config) { var _a, _b, _c, _d, _e; const gridComponent = this.config.components.grid; gridComponent.visible = (_a = config.visible) !== null && _a !== void 0 ? _a : false; gridComponent.dash = (_b = config.dash) !== null && _b !== void 0 ? _b : [0, 0]; gridComponent.width = (_c = config.width) !== null && _c !== void 0 ? _c : 1; gridComponent.color = (_d = config.color) !== null && _d !== void 0 ? _d : '#FFFFFF'; this.config.colors.chartAreaTheme.gridColor = (_e = config.color) !== null && _e !== void 0 ? _e : '#FFFFFF'; this.redraw(); } /** * Sets the visibility of the grid component. * @param {boolean} visible - A boolean value indicating whether the grid should be visible or not. * @returns {void} */ setGridVisible(visible) { if (this.config.components && this.config.components.grid) { this.config.components.grid.visible = visible; this.redraw(); } } /** * Sets the visibility of the vertical grid lines in the grid component. * @param {boolean} showVertical - A boolean value indicating whether to show or hide the vertical grid lines. * @returns {void} */ setGridVertical(showVertical) { this.config.components.grid.vertical = showVertical; this.redraw(); } /** * Sets the visibility of the horizontal grid lines in the chart. * @param {boolean} showHorizontal - A boolean value indicating whether to show or hide the horizontal grid lines. * @returns {void} */ setGridHorizontal(showHorizontal) { this.config.components.grid.horizontal = showHorizontal; this.redraw(); } /** * This method triggers the 'draw' event on the 'bus' object. * @returns {void} */ redraw() { this.bus.fireDraw(); } /** * Returns the offsets of the chart components from the chart configuration object. * * @returns {ChartConfigComponentsOffsets} The offsets of the chart components. */ getOffsets() { return this.config.components && this.config.components.offsets; } /** * Sets the visibility of the borders of the candles in the chart. * @param {boolean} show - A boolean value indicating whether to show or hide the borders of the candles. Default value is true. * @returns {void} */ setShowCandleBorders(show = true) { if (this.config.components && this.config.components.chart) { this.config.components.chart.showCandlesBorder = show; this.redraw(); } } /** * Sets the visibility of the high-low component. * @param {boolean} visible - Whether the high-low component should be visible or not. Default is true. * @returns {void} */ setHighLowVisible(visible = true) { if (this.config.components && this.config.components.highLow) { this.config.components.highLow.visible = visible; this.redraw(); } } /** * Sets the visibility of the cross tool component. * @param {string} type - The type of cross tool to be displayed. Default value is 'cross-and-labels'. * @returns {void} */ setCrossToolVisible(type = 'cross-and-labels') { if (this.config.components && this.config.components.crossTool) { this.config.components.crossTool.type = type; this.redraw(); } } /** * @deprecated use {@link highlightsComponent.setHighlightsVisible} instead */ setHighlightsVisible(visible = true) { var _a; if ((_a = this.config.components) === null || _a === void 0 ? void 0 : _a.highlights) { this.config.components.highlights.visible = visible; this.redraw(); } } /** * @deprecated use {@link highlightsComponent.setHighlights} instead */ setHighlightsData(data) { this.highlightsComponent.setHighlights(data); } /** * Sets the colors of the chart. * * @param {DeepPartial<ChartColors>} colors - An object containing the colors to be set. * @returns {void} */ setColors(colors) { merge(this.config.colors, colors, { addIfMissing: true, overrideExisting: true, }); this.redraw(); } /** * Adds a mouse move event listener to a canvas element. * @param {string} canvasElement - The ID of the canvas element to add the listener to. * @param {function} handler - The function to be called when the mouse moves over the canvas element. * @returns {function} - A function that removes the event listener when called. */ addMouseMoveOnChartElementHandler(canvasElement, handler) { const elementBoundsHitTest = this.canvasBoundsContainer.getBoundsHitTest(canvasElement); const subscription = this.canvasInputListener.observeMouseMove(elementBoundsHitTest).subscribe(handler); return () => subscription.unsubscribe(); } /** * Adds a mouse enter event handler to a chart element. * * @param {string} canvasElement - The ID of the canvas element to attach the event handler to. * @param {(mouseEnter: boolean) => void} handler - The function to be called when the mouse enters or leaves the element. * @param {boolean} [skipWhileDragging=false] - Whether to skip the event while the user is dragging the chart. * @returns {Function} - A function that can be called to unsubscribe from the event. */ addMouseEnterOnChartElementHandler(canvasElement, handler, skipWhileDragging = false) { const elementBoundsHitTest = this.canvasBoundsContainer.getBoundsHitTest(canvasElement); const subscription = this.canvasInputListener .observeMouseEnter(elementBoundsHitTest, skipWhileDragging) .subscribe(handler); return () => subscription.unsubscribe(); } /** * Adds a click event listener to a chart element on the canvas. * @param {string} canvasElement - The element on the canvas to add the click event listener to. * @param {function} handler - The function to be called when the element is clicked. It takes a Point object as a parameter. * @returns {function} - A function that can be called to unsubscribe the click event listener. */ addClickOnChartElementHandler(canvasElement, handler) { const elementBoundsHitTest = this.canvasBoundsContainer.getBoundsHitTest(canvasElement); const subscription = this.canvasInputListener.observeClick(elementBoundsHitTest).subscribe(handler); return () => subscription.unsubscribe(); } /** * Adds a drag event listener to the specified canvas element. * @param {string} canvasElement - The ID of the canvas element to add the listener to. * @param {(xPosition: number) => void} handler - The function to be called when a drag event occurs. It takes the x position of the drag event as a parameter. * @returns {() => void} - A function that can be called to unsubscribe from the drag event listener. */ addDragEventsListener(canvasElement, handler) { const elementBoundsHitTest = this.canvasBoundsContainer.getBoundsHitTest(canvasElement); const subscription = mergeRx(this.canvasInputListener.observeYDrag(elementBoundsHitTest), this.canvasInputListener.observeXDrag(elementBoundsHitTest)).subscribe(handler); return () => subscription.unsubscribe(); } /** * Registers a chart component and includes it in the the chart lifecycle * @param initComponent - a function for component init * @param onComponentInit - will be called after component init */ registerComponent(initComponent, onComponentInit) { const component = initComponent(this); this.components.push(component); onComponentInit && onComponentInit(component); component.activate(); } }