@grptx/ng-canvas-gauges
Version:
Angular 14+ component wrapper for the canvas-gauges lib written by @Mikhus
378 lines (370 loc) • 15.5 kB
JavaScript
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