UNPKG

@grptx/ng-canvas-gauges

Version:

Angular 14+ component wrapper for the canvas-gauges lib written by @Mikhus

378 lines (370 loc) 15.5 kB
import * as i0 from '@angular/core'; import { Directive, ViewChild, Input, Component, NgModule } from '@angular/core'; import * as CanvasGauges from 'canvas-gauges'; import * as Rx from 'rx-dom-html'; /*! * The MIT License (MIT) * * Copyright (c) 2017 Vlad Martynenko <vladimir.martynenko.work@gmail.com> * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ const _c0 = ["gauge"]; // String utils const toCamelCase = (str) => str.replace(/(\-\w)/g, (matches) => matches[1].toUpperCase()); const toKebabCase = (str) => str.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase(); const attributeName2PropertyName = (attrName) => toCamelCase(attrName); /** * Base gauge component for the Gauges rendering * T - Type of the Gauge to be rendered (Currently RadialGauge, LinearGauge from the original library) * T2 - Type of config options used by the particular gauge (RadialGaugeOptions, LinearGaugeOptions) */ class BaseGauge { /** * * @param el - reference to the element of the whole component, used to scrape options declared on the component itself * @param zone - required to redraw gauge outside of Angular, due to animation lags caused by the ovewritten function of the ngZone */ constructor(el, zone) { this.el = el; this.zone = zone; /** * Flag indicating that OnViewInit life-cycle has completed */ this.isInited = false; } /** * Returns gauges properties as an options object. * Option properties consist of the attribute-based properties and those * explicitly set. * @returns <T2> */ get options() { const options = {}; options.renderTo = this.canvas.nativeElement; // Map attribute-based options onto options. // Requries converting kebab style attribute names to camelCase property names for (const attr of this.el.nativeElement.attributes) { const prop = attributeName2PropertyName(attr.name); options[prop] = CanvasGauges.DomObserver.parse(attr.value); } // merge preOptons with attribute-based properties // tslint:disable-next-line:forin for (const prop in this.preInitOptions) { options[prop] = this.preInitOptions[prop]; } // clear the preInitOptions as they have already been merged // with the attribute-based properties if (this.isInited) { this.preInitOptions = null; } return options; } /** * Assign gauge options at anytime in the lifecycle. * @param newOptions - assign the style and size properties */ set options(newOptions) { // cache newOptions as preInitOptions until gauge is ready if (!this.isInited) { this.preInitOptions = newOptions; return; } this.update(newOptions); } /** * Assign the value of the gauge visual indicator such as a needle or pointer * @param newValue the guage new value */ set value(newValue) { // case new gauge value as preInitValue until the gauge is ready if (!this.isInited) { this.preInitValue = newValue; return; } this.zone.runOutsideAngular(() => { this.gauge.value = newValue; }); } /** * Update the gauge options. Do not use until after OnViewInit() before using. * * Special implementation note - options.properties are maintained as * attribute name->value on this component's elementRef. Thus this method * maps each newOptions property onto the property's corresponding attribute. * The attribute update triggers a DOM mutation event which "this" listens for. * See #listenForDOMEvents() * * @param newOptions - the options to update the gauge */ update(newOptions) { // map all options onto this element's attributes // Then attribute changes will be detected and pushed to the gauge.update() if (!newOptions) { return; } // tslint:disable-next-line:forin for (const prop in newOptions) { const val = newOptions[prop].toString(); if (prop === 'value') { // short circuit the value property update by calling // the gauge.value api directly for efficient animated update this.value = CanvasGauges.DomObserver.parse(val); } else { const attrName = toKebabCase(prop); this.el.nativeElement.setAttribute(attrName, val); } } } /** * Perform gauge initialization. * Subclasses that override this method must this super version * for proper operation. */ ngAfterViewInit() { // initial update of gauge properties this.initGauge(); this.listenForDOMEvents(); this.isInited = true; } /** * Listen for attribute-change events that are created when updating * the options of this gauge. */ listenForDOMEvents() { // Listen to gauge element for attribute changes // Convert all changed attribtues into a GenericOptions or subclass // Update the gauge with the new options. this.domListener = Rx.DOM.fromMutationObserver(this.el.nativeElement, { attributes: true }). subscribe(changes => { const newOptions = {}; changes.forEach(change => { if ('attributes' === change.type) { // console.log('DOM, change', change.attributeName); newOptions[attributeName2PropertyName(change.attributeName)] = CanvasGauges.DomObserver.parse(this.el.nativeElement.getAttribute(change.attributeName)); } }); this.basicUpdate(newOptions); }); } /** * Discontinue listening for attribute change events. */ stopListeningForDOMEvents() { if (this.domListener) { this.domListener.disconnect(); this.domListener = null; } } /** * Initalize the gauge with all options defined by attributes and * parent component options. */ initGauge() { const options = this.options; if (this.preInitValue) { options.value = this.preInitValue; } // init options.renderTo if needed if (!options.hasOwnProperty('renderTo') || !options.renderTo) { options.renderTo = this.canvas.nativeElement; } this.basicUpdate(options); } /** * Performs the gauge update using the current options * @param options The options for the guage */ basicUpdate(options) { // treat the value property special and update it through the // value getter. if (typeof options.value === 'number') { // use gauge api directly for most efficient update method this.value = options.value; // filter value property from options to avoid redundant // processing by gauge delete options.value; } // do nothing if no option properties to update if (Object.keys(options).length) { this.gauge.update(options); } } } BaseGauge.ɵfac = function BaseGauge_Factory(t) { return new (t || BaseGauge)(i0.ɵɵdirectiveInject(i0.ElementRef), i0.ɵɵdirectiveInject(i0.NgZone)); }; BaseGauge.ɵdir = /*@__PURE__*/ i0.ɵɵdefineDirective({ type: BaseGauge, viewQuery: function BaseGauge_Query(rf, ctx) { if (rf & 1) { i0.ɵɵviewQuery(_c0, 7); } if (rf & 2) { let _t; i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.canvas = _t.first); } }, inputs: { options: "options", value: "value" } }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(BaseGauge, [{ type: Directive }], function () { return [{ type: i0.ElementRef }, { type: i0.NgZone }]; }, { canvas: [{ type: ViewChild, args: ['gauge', { static: true }] }], options: [{ type: Input }], value: [{ type: Input }] }); })(); /*! * The MIT License (MIT) * * Copyright (c) 2017 Vlad Martynenko <vladimir.martynenko.work@gmail.com> * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ /** * Implements Linear Gauge from the original library */ // tslint:disable-next-line:component-class-suffix class LinearGauge extends BaseGauge { constructor(el, zone) { super(el, zone); } ngOnInit() { this.gauge = new CanvasGauges.LinearGauge(this.options).draw(); } } LinearGauge.ɵfac = function LinearGauge_Factory(t) { return new (t || LinearGauge)(i0.ɵɵdirectiveInject(i0.ElementRef), i0.ɵɵdirectiveInject(i0.NgZone)); }; LinearGauge.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: LinearGauge, selectors: [["linear-gauge"]], features: [i0.ɵɵInheritDefinitionFeature], decls: 2, vars: 0, consts: [["gauge", ""]], template: function LinearGauge_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelement(0, "canvas", null, 0); } }, encapsulation: 2 }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(LinearGauge, [{ type: Component, args: [{ // tslint:disable-next-line:component-selector selector: 'linear-gauge', template: '<canvas #gauge></canvas>' }] }], function () { return [{ type: i0.ElementRef }, { type: i0.NgZone }]; }, null); })(); /*! * The MIT License (MIT) * * Copyright (c) 2017 Vlad Martynenko <vladimir.martynenko.work@gmail.com> * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ /** * Implements Radial Gauge from the original library */ // tslint:disable-next-line:component-class-suffix class RadialGauge extends BaseGauge { constructor(el, zone) { super(el, zone); } ngOnInit() { this.gauge = new CanvasGauges.RadialGauge(this.options).draw(); } } RadialGauge.ɵfac = function RadialGauge_Factory(t) { return new (t || RadialGauge)(i0.ɵɵdirectiveInject(i0.ElementRef), i0.ɵɵdirectiveInject(i0.NgZone)); }; RadialGauge.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: RadialGauge, selectors: [["radial-gauge"]], features: [i0.ɵɵInheritDefinitionFeature], decls: 2, vars: 0, consts: [["gauge", ""]], template: function RadialGauge_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelement(0, "canvas", null, 0); } }, encapsulation: 2 }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(RadialGauge, [{ type: Component, args: [{ // tslint:disable-next-line:component-selector selector: 'radial-gauge', template: '<canvas #gauge></canvas>' }] }], function () { return [{ type: i0.ElementRef }, { type: i0.NgZone }]; }, null); })(); class GaugesModule { } GaugesModule.ɵfac = function GaugesModule_Factory(t) { return new (t || GaugesModule)(); }; GaugesModule.ɵmod = /*@__PURE__*/ i0.ɵɵdefineNgModule({ type: GaugesModule }); GaugesModule.ɵinj = /*@__PURE__*/ i0.ɵɵdefineInjector({}); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(GaugesModule, [{ type: NgModule, args: [{ declarations: [ LinearGauge, RadialGauge ], imports: [], exports: [ LinearGauge, RadialGauge ] }] }], null, null); })(); (function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(GaugesModule, { declarations: [LinearGauge, RadialGauge], exports: [LinearGauge, RadialGauge] }); })(); /* * Public API Surface of ng-canvas-gauges */ /** * Generated bundle index. Do not edit. */ export { GaugesModule, LinearGauge, RadialGauge }; //# sourceMappingURL=grptx-ng-canvas-gauges.mjs.map