UNPKG

veritatisquam

Version:
238 lines (204 loc) 5.95 kB
// Internal Imports import { ChartConfig, BaseChartOptions, LayoutGrowth, LayoutDirection, LegendOrientations, Events as ChartEvents, } from './interfaces'; // Misc import { ChartModel } from './model'; import { Component, Title, Legend, LayoutComponent, Tooltip, Spacer, } from './components'; import { Tools } from './tools'; // Services import { DOMUtils, Events, GradientUtils, Transitions } from './services/index'; export class Chart { components: Component[]; services: any = { domUtils: DOMUtils, events: Events, gradientUtils: GradientUtils, transitions: Transitions, }; model: ChartModel = new ChartModel(this.services); constructor(holder: Element, chartConfigs: ChartConfig<BaseChartOptions>) {} // Contains the code that uses properties that are overridable by the super-class init(holder: Element, chartConfigs: ChartConfig<BaseChartOptions>) { // Store the holder in the model this.model.set({ holder }, { skipUpdate: true }); // Initialize all services Object.keys(this.services).forEach((serviceName) => { const serviceObj = this.services[serviceName]; this.services[serviceName] = new serviceObj( this.model, this.services ); }); // Call update() when model has been updated this.services.events.addEventListener(ChartEvents.Model.UPDATE, (e) => { const animate = !!Tools.getProperty(e, 'detail', 'animate'); this.update(animate); }); // Set model data & options this.model.setData(chartConfigs.data); // Set chart resize event listener this.services.events.addEventListener(ChartEvents.Chart.RESIZE, () => { this.update(false); }); this.components = this.getComponents(); this.update(); } getComponents(): any[] { console.error('getComponents() method is not implemented'); return null; } update(animate = true) { if (!this.components) { return; } // Update all services Object.keys(this.services).forEach((serviceName) => { const serviceObj = this.services[serviceName]; serviceObj.update(); }); // Render all components this.components.forEach((component) => component.render(animate)); // Asynchronously dispatch a "render-finished" event // This is needed because of d3-transitions // Since at the start of the transition // Elements do not hold their final size or position const pendingTransitions = this.services.transitions.getPendingTransitions(); const promises = Object.keys(pendingTransitions).map((transitionID) => { const transition = pendingTransitions[transitionID]; return transition.end().catch((e) => e); // Skip rejects since we don't care about those; }); Promise.all(promises).then(() => this.services.events.dispatchEvent( ChartEvents.Chart.RENDER_FINISHED ) ); } destroy() { // Call the destroy() method on all components this.components.forEach((component) => component.destroy()); // Remove the chart holder this.services.domUtils.getHolder().remove(); this.model.set({ destroyed: true }, { skipUpdate: true }); } protected getChartComponents(graphFrameComponents: any[]) { const titleComponent = { id: 'title', components: [new Title(this.model, this.services)], growth: { x: LayoutGrowth.PREFERRED, y: LayoutGrowth.FIXED, }, }; const legendComponent = { id: 'legend', components: [new Legend(this.model, this.services)], growth: { x: LayoutGrowth.PREFERRED, y: LayoutGrowth.FIXED, }, }; const graphFrameComponent = { id: 'graph-frame', components: graphFrameComponents, growth: { x: LayoutGrowth.STRETCH, y: LayoutGrowth.FIXED, }, }; const isLegendEnabled = this.model.getOptions().legend.enabled !== false; // TODORF - REUSE BETWEEN AXISCHART & CHART // Decide the position of the legend in reference to the chart let fullFrameComponentDirection = LayoutDirection.COLUMN; if (isLegendEnabled) { const legendPosition = Tools.getProperty( this.model.getOptions(), 'legend', 'position' ); if (legendPosition === 'left') { fullFrameComponentDirection = LayoutDirection.ROW; if (!this.model.getOptions().legend.orientation) { this.model.getOptions().legend.orientation = LegendOrientations.VERTICAL; } } else if (legendPosition === 'right') { fullFrameComponentDirection = LayoutDirection.ROW_REVERSE; if (!this.model.getOptions().legend.orientation) { this.model.getOptions().legend.orientation = LegendOrientations.VERTICAL; } } else if (legendPosition === 'bottom') { fullFrameComponentDirection = LayoutDirection.COLUMN_REVERSE; } } const legendSpacerComponent = { id: 'spacer', components: [new Spacer(this.model, this.services)], growth: { x: LayoutGrowth.PREFERRED, y: LayoutGrowth.FIXED, }, }; const fullFrameComponent = { id: 'full-frame', components: [ new LayoutComponent( this.model, this.services, [ ...(isLegendEnabled ? [legendComponent] : []), ...(isLegendEnabled ? [legendSpacerComponent] : []), graphFrameComponent, ], { direction: fullFrameComponentDirection, } ), ], growth: { x: LayoutGrowth.STRETCH, y: LayoutGrowth.FIXED, }, }; // Add chart title if it exists const topLevelLayoutComponents = []; if (this.model.getOptions().title) { topLevelLayoutComponents.push(titleComponent); const titleSpacerComponent = { id: 'spacer', components: [new Spacer(this.model, this.services)], growth: { x: LayoutGrowth.PREFERRED, y: LayoutGrowth.FIXED, }, }; topLevelLayoutComponents.push(titleSpacerComponent); } topLevelLayoutComponents.push(fullFrameComponent); return [ new Tooltip(this.model, this.services), new LayoutComponent( this.model, this.services, topLevelLayoutComponents, { direction: LayoutDirection.COLUMN, } ), ]; } }