From 8771a6f7540fce7d12e12485ec22d166c5ed455b Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 13 Feb 2020 12:52:44 +0200 Subject: [PATCH] Implement Analogue gauges --- ui-ngx/package-lock.json | 11 + ui-ngx/package.json | 2 + .../widget/lib/analogue-compass.models.ts | 357 ++++++ .../components/widget/lib/analogue-compass.ts | 108 ++ .../widget/lib/analogue-gauge.models.ts | 1039 +++++++++++++++++ .../lib/analogue-linear-gauge.models.ts | 88 ++ .../widget/lib/analogue-linear-gauge.ts | 65 ++ .../lib/analogue-radial-gauge.models.ts | 52 + .../widget/lib/analogue-radial-gauge.ts | 64 + .../components/widget/lib/settings.models.ts | 38 + .../widget/widget-component.service.ts | 12 - .../json-form/json-form.component.ts | 4 +- .../json-form/react/json-form-array.tsx | 4 +- .../react/json-form-base-component.tsx | 4 +- .../json-form/react/json-form-radios.tsx | 5 +- .../json-form/react/json-form-schema-form.tsx | 4 +- .../json-form/react/json-form-text.tsx | 4 +- .../json-form/react/json-form.models.ts | 4 +- ui-ngx/src/polyfills.ts | 21 + ui-ngx/src/scss/fonts.scss | 21 + ui-ngx/src/styles.scss | 1 + ui-ngx/src/tsconfig.app.json | 2 +- 22 files changed, 1884 insertions(+), 26 deletions(-) create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/analogue-compass.models.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/analogue-compass.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/analogue-gauge.models.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/analogue-linear-gauge.models.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/analogue-linear-gauge.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/analogue-radial-gauge.models.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/analogue-radial-gauge.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings.models.ts create mode 100644 ui-ngx/src/scss/fonts.scss diff --git a/ui-ngx/package-lock.json b/ui-ngx/package-lock.json index 8951973946..46da932a41 100644 --- a/ui-ngx/package-lock.json +++ b/ui-ngx/package-lock.json @@ -3037,6 +3037,12 @@ } } }, + "@types/canvas-gauges": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@types/canvas-gauges/-/canvas-gauges-2.1.2.tgz", + "integrity": "sha512-oWCq0XjsTBXPtMKXoW23ORbMWguC2Fa8o5NiZVYiUoQMMrpNLKj1E+LDznlMpcib3iyWVIy+TEpc/ea6LMbW3Q==", + "dev": true + }, "@types/estree": { "version": "0.0.42", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.42.tgz", @@ -4435,6 +4441,11 @@ "integrity": "sha512-feylzsbDxi1gPZ1IjystzIQZagYYLvfKrSuygUCgf7z6x790VEzze5QEkdSV1U58RA7Hi0+v6fv4K54atOzATg==", "dev": true }, + "canvas-gauges": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/canvas-gauges/-/canvas-gauges-2.1.5.tgz", + "integrity": "sha512-7GUd1uukePQPQPIoM8Sh4UrG8om+2RG+D8WN5BCkIp9wAfByzPuZZinsUkfFCyRrEOZ/rhuwBfFnb1ld8IfNrw==" + }, "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", diff --git a/ui-ngx/package.json b/ui-ngx/package.json index c5b7284a83..1e4b937cde 100644 --- a/ui-ngx/package.json +++ b/ui-ngx/package.json @@ -42,6 +42,7 @@ "angular-gridster2": "^9.0.0", "angular2-hotkeys": "^2.1.5", "base64-js": "^1.3.1", + "canvas-gauges": "^2.1.5", "compass-sass-mixins": "^0.12.7", "core-js": "^3.6.4", "date-fns": "^2.9.0", @@ -91,6 +92,7 @@ "@angular/cli": "^9.0.1", "@angular/compiler-cli": "~9.0.0", "@angular/language-service": "~9.0.0", + "@types/canvas-gauges": "^2.1.2", "@types/flot": "0.0.31", "@types/jasmine": "^3.5.3", "@types/jasminewd2": "~2.0.8", diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/analogue-compass.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/analogue-compass.models.ts new file mode 100644 index 0000000000..724d300d3b --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/analogue-compass.models.ts @@ -0,0 +1,357 @@ +/// +/// Copyright © 2016-2019 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { JsonSettingsSchema } from '@shared/models/widget.models'; +import { FontSettings } from '@home/components/widget/lib/settings.models'; +import { AnimationRule, AnimationTarget } from '@home/components/widget/lib/analogue-gauge.models'; + +export interface AnalogueCompassSettings { + majorTicks: string[]; + minorTicks: number; + showStrokeTicks: boolean; + needleCircleSize: number; + showBorder: boolean; + borderOuterWidth: number; + colorPlate: string; + colorMajorTicks: string; + colorMinorTicks: string; + colorNeedle: string; + colorNeedleCircle: string; + colorBorder: string; + majorTickFont: FontSettings; + animation: boolean; + animationDuration: number; + animationRule: AnimationRule; + animationTarget: AnimationTarget; +} + +export const analogueCompassSettingsSchema: JsonSettingsSchema = { + schema: { + type: 'object', + title: 'Settings', + properties: { + majorTicks: { + title: 'Major ticks names', + type: 'array', + items: { + title: 'Tick name', + type: 'string' + } + }, + minorTicks: { + title: 'Minor ticks count', + type: 'number', + default: 22 + }, + showStrokeTicks: { + title: 'Show ticks stroke', + type: 'boolean', + default: false + }, + needleCircleSize: { + title: 'Needle circle size', + type: 'number', + default: 15 + }, + showBorder: { + title: 'Show border', + type: 'boolean', + default: true + }, + borderOuterWidth: { + title: 'Border width', + type: 'number', + default: 10 + }, + colorPlate: { + title: 'Plate color', + type: 'string', + default: '#222' + }, + colorMajorTicks: { + title: 'Major ticks color', + type: 'string', + default: '#f5f5f5' + }, + colorMinorTicks: { + title: 'Minor ticks color', + type: 'string', + default: '#ddd' + }, + colorNeedle: { + title: 'Needle color', + type: 'string', + default: '#f08080' + }, + colorNeedleCircle: { + title: 'Needle circle color', + type: 'string', + default: '#e8e8e8' + }, + colorBorder: { + title: 'Border color', + type: 'string', + default: '#ccc' + }, + majorTickFont: { + title: 'Major tick font', + type: 'object', + properties: { + family: { + title: 'Font family', + type: 'string', + default: 'Roboto' + }, + size: { + title: 'Size', + type: 'number', + default: 20 + }, + style: { + title: 'Style', + type: 'string', + default: 'normal' + }, + weight: { + title: 'Weight', + type: 'string', + default: '500' + }, + color: { + title: 'color', + type: 'string', + default: '#ccc' + } + } + }, + animation: { + title: 'Enable animation', + type: 'boolean', + default: true + }, + animationDuration: { + title: 'Animation duration', + type: 'number', + default: 500 + }, + animationRule: { + title: 'Animation rule', + type: 'string', + default: 'cycle' + }, + animationTarget: { + title: 'Animation target', + type: 'string', + default: 'needle' + } + }, + required: [] + }, + form: [ + { + key: 'majorTicks', + items:[ + 'majorTicks[]' + ] + }, + 'minorTicks', + 'showStrokeTicks', + 'needleCircleSize', + 'showBorder', + 'borderOuterWidth', + { + key: 'colorPlate', + type: 'color' + }, + { + key: 'colorMajorTicks', + type: 'color' + }, + { + key: 'colorMinorTicks', + type: 'color' + }, + { + key: 'colorNeedle', + type: 'color' + }, + { + key: 'colorNeedleCircle', + type: 'color' + }, + { + key: 'colorBorder', + type: 'color' + }, + { + key: 'majorTickFont', + items: [ + 'majorTickFont.family', + 'majorTickFont.size', + { + key: 'majorTickFont.style', + type: 'rc-select', + multiple: false, + items: [ + { + value: 'normal', + label: 'Normal' + }, + { + value: 'italic', + label: 'Italic' + }, + { + value: 'oblique', + label: 'Oblique' + } + ] + }, + { + key: 'majorTickFont.weight', + type: 'rc-select', + multiple: false, + items: [ + { + value: 'normal', + label: 'Normal' + }, + { + value: 'bold', + label: 'Bold' + }, + { + value: 'bolder', + label: 'Bolder' + }, + { + value: 'lighter', + label: 'Lighter' + }, + { + value: '100', + label: '100' + }, + { + value: '200', + label: '200' + }, + { + value: '300', + label: '300' + }, + { + value: '400', + label: '400' + }, + { + value: '500', + label: '500' + }, + { + value: '600', + label: '600' + }, + { + value: '700', + label: '800' + }, + { + value: '800', + label: '800' + }, + { + value: '900', + label: '900' + } + ] + }, + { + key: 'majorTickFont.color', + type: 'color' + } + ] + }, + 'animation', + 'animationDuration', + { + key: 'animationRule', + type: 'rc-select', + multiple: false, + items: [ + { + value: 'linear', + label: 'Linear' + }, + { + value: 'quad', + label: 'Quad' + }, + { + value: 'quint', + label: 'Quint' + }, + { + value: 'cycle', + label: 'Cycle' + }, + { + value: 'bounce', + label: 'Bounce' + }, + { + value: 'elastic', + label: 'Elastic' + }, + { + value: 'dequad', + label: 'Dequad' + }, + { + value: 'dequint', + label: 'Dequint' + }, + { + value: 'decycle', + label: 'Decycle' + }, + { + value: 'debounce', + label: 'Debounce' + }, + { + value: 'delastic', + label: 'Delastic' + } + ] + }, + { + key: 'animationTarget', + type: 'rc-select', + multiple: false, + items: [ + { + value: 'needle', + label: 'Needle' + }, + { + value: 'plate', + label: 'Plate' + } + ] + } + ] +}; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/analogue-compass.ts b/ui-ngx/src/app/modules/home/components/widget/lib/analogue-compass.ts new file mode 100644 index 0000000000..1bccd4aabc --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/analogue-compass.ts @@ -0,0 +1,108 @@ +/// +/// Copyright © 2016-2019 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { WidgetContext } from '@home/models/widget-component.models'; +import * as CanvasGauges from 'canvas-gauges'; +import { + AnalogueCompassSettings, + analogueCompassSettingsSchema +} from '@home/components/widget/lib/analogue-compass.models'; +import { deepClone, isDefined } from '@core/utils'; +import { JsonSettingsSchema } from '@shared/models/widget.models'; +import { getFontFamily } from '@home/components/widget/lib/settings.models'; +import { TbBaseGauge } from '@home/components/widget/lib/analogue-gauge.models'; +import RadialGaugeOptions = CanvasGauges.RadialGaugeOptions; +import BaseGauge = CanvasGauges.BaseGauge; +import RadialGauge = CanvasGauges.RadialGauge; + +const analogueCompassSettingsSchemaValue = analogueCompassSettingsSchema; + +export class TbAnalogueCompass extends TbBaseGauge { + + static get settingsSchema(): JsonSettingsSchema { + return analogueCompassSettingsSchemaValue; + } + + constructor(ctx: WidgetContext, canvasId: string) { + super(ctx, canvasId); + } + + protected createGaugeOptions(gaugeElement: HTMLElement, settings: AnalogueCompassSettings): RadialGaugeOptions { + + const majorTicks = (settings.majorTicks && settings.majorTicks.length > 0) ? deepClone(settings.majorTicks) : + ['N','NE','E','SE','S','SW','W','NW']; + majorTicks.push(majorTicks[0]); + + return { + renderTo: gaugeElement, + + // Generic options + minValue: 0, + maxValue: 360, + majorTicks, + minorTicks: settings.minorTicks || 22, + ticksAngle: 360, + startAngle: 180, + strokeTicks: settings.showStrokeTicks || false, + highlights: [], + valueBox: false, + + // needle + needleCircleSize: settings.needleCircleSize || 15, + needleType: 'line', + needleStart: 75, + needleEnd: 99, + needleWidth: 3, + needleCircleOuter: false, + + // borders + borders: settings.showBorder || false, + borderInnerWidth: 0, + borderMiddleWidth: 0, + borderOuterWidth: settings.borderOuterWidth || 10, + borderShadowWidth: 0, + + // colors + colorPlate: settings.colorPlate || '#222', + colorMajorTicks: settings.colorMajorTicks || '#f5f5f5', + colorMinorTicks: settings.colorMinorTicks || '#ddd', + colorNeedle: settings.colorNeedle || '#f08080', + colorNeedleEnd: settings.colorNeedle || '#f08080', + colorNeedleCircleInner: settings.colorNeedleCircle || '#e8e8e8', + colorNeedleCircleInnerEnd: settings.colorNeedleCircle || '#e8e8e8', + colorBorderOuter: settings.colorBorder || '#ccc', + colorBorderOuterEnd: settings.colorBorder || '#ccc', + colorNeedleShadowDown: '#222', + + // fonts + fontNumbers: getFontFamily(settings.majorTickFont), + fontNumbersSize: settings.majorTickFont && settings.majorTickFont.size ? settings.majorTickFont.size : 20, + fontNumbersStyle: settings.majorTickFont && settings.majorTickFont.style ? settings.majorTickFont.style : 'normal', + fontNumbersWeight: settings.majorTickFont && settings.majorTickFont.weight ? settings.majorTickFont.weight : '500', + colorNumbers: settings.majorTickFont && settings.majorTickFont.color ? settings.majorTickFont.color : '#ccc', + + // animations + animation: settings.animation !== false && !this.ctx.isMobile, + animationDuration: (isDefined(settings.animationDuration) && settings.animationDuration !== null) ? settings.animationDuration : 500, + animationRule: settings.animationRule || 'cycle', + animationTarget: settings.animationTarget || 'needle' + }; + } + + protected createGauge(gaugeData: RadialGaugeOptions): BaseGauge { + return new RadialGauge(gaugeData); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/analogue-gauge.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/analogue-gauge.models.ts new file mode 100644 index 0000000000..bd0d4f9837 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/analogue-gauge.models.ts @@ -0,0 +1,1039 @@ +/// +/// Copyright © 2016-2019 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + + +import { FontSettings, getFontFamily } from '@home/components/widget/lib/settings.models'; +import { JsonSettingsSchema } from '@shared/models/widget.models'; +import { WidgetContext } from '@home/models/widget-component.models'; +import { isDefined } from '@core/utils'; +import * as tinycolor_ from 'tinycolor2'; +import Highlight = CanvasGauges.Highlight; +import BaseGauge = CanvasGauges.BaseGauge; +import GenericOptions = CanvasGauges.GenericOptions; + +const tinycolor = tinycolor_; + +export type AnimationRule = 'linear' | 'quad' | 'quint' | 'cycle' + | 'bounce' | 'elastic' | 'dequad' | 'dequint' + | 'decycle' | 'debounce' | 'delastic'; + +export type AnimationTarget = 'needle' | 'plate'; + +export interface AnalogueGaugeSettings { + minValue: number; + maxValue: number; + unitTitle: string; + showUnitTitle: boolean; + majorTicksCount: number; + minorTicks: number; + valueBox: boolean; + valueInt: number; + valueDec?: number; + units?: string; + defaultColor: string; + colorPlate: string; + colorMajorTicks: string; + colorMinorTicks: string; + colorNeedle: string; + colorNeedleEnd: string; + colorNeedleShadowUp: string; + colorNeedleShadowDown: string; + colorValueBoxRect: string; + colorValueBoxRectEnd: string; + colorValueBoxBackground: string; + colorValueBoxShadow: string; + highlights: Highlight[]; + highlightsWidth: number; + showBorder: boolean; + numbersFont: FontSettings; + titleFont: FontSettings; + unitsFont: FontSettings; + valueFont: FontSettings; + animation: boolean; + animationDuration: number; + animationRule: AnimationRule; +} + +export const analogueGaugeSettingsSchema: JsonSettingsSchema = { + schema: { + type: 'object', + title: 'Settings', + properties: { + minValue: { + title: 'Minimum value', + type: 'number', + default: 0 + }, + maxValue: { + title: 'Maximum value', + type: 'number', + default: 100 + }, + unitTitle: { + title: 'Unit title', + type: 'string', + default: null + }, + showUnitTitle: { + title: 'Show unit title', + type: 'boolean', + default: true + }, + majorTicksCount: { + title: 'Major ticks count', + type: 'number', + default: null + }, + minorTicks: { + title: 'Minor ticks count', + type: 'number', + default: 2 + }, + valueBox: { + title: 'Show value box', + type: 'boolean', + default: true + }, + valueInt: { + title: 'Digits count for integer part of value', + type: 'number', + default: 3 + }, + defaultColor: { + title: 'Default color', + type: 'string', + default: null + }, + colorPlate: { + title: 'Plate color', + type: 'string', + default: '#fff' + }, + colorMajorTicks: { + title: 'Major ticks color', + type: 'string', + default: '#444' + }, + colorMinorTicks: { + title: 'Minor ticks color', + type: 'string', + default: '#666' + }, + colorNeedle: { + title: 'Needle color', + type: 'string', + default: null + }, + colorNeedleEnd: { + title: 'Needle color - end gradient', + type: 'string', + default: null + }, + colorNeedleShadowUp: { + title: 'Upper half of the needle shadow color', + type: 'string', + default: 'rgba(2,255,255,0.2)' + }, + colorNeedleShadowDown: { + title: 'Drop shadow needle color.', + type: 'string', + default: 'rgba(188,143,143,0.45)' + }, + colorValueBoxRect: { + title: 'Value box rectangle stroke color', + type: 'string', + default: '#888' + }, + colorValueBoxRectEnd: { + title: 'Value box rectangle stroke color - end gradient', + type: 'string', + default: '#666' + }, + colorValueBoxBackground: { + title: 'Value box background color', + type: 'string', + default: '#babab2' + }, + colorValueBoxShadow: { + title: 'Value box shadow color', + type: 'string', + default: 'rgba(0,0,0,1)' + }, + highlights: { + title: 'Highlights', + type: 'array', + items: { + title: 'Highlight', + type: 'object', + properties: { + from: { + title: 'From', + type: 'number' + }, + to: { + title: 'To', + type: 'number' + }, + color: { + title: 'Color', + type: 'string' + } + } + } + }, + highlightsWidth: { + title: 'Highlights width', + type: 'number', + default: 15 + }, + showBorder: { + title: 'Show border', + type: 'boolean', + default: true + }, + numbersFont: { + title: 'Tick numbers font', + type: 'object', + properties: { + family: { + title: 'Font family', + type: 'string', + default: 'Roboto' + }, + size: { + title: 'Size', + type: 'number', + default: 18 + }, + style: { + title: 'Style', + type: 'string', + default: 'normal' + }, + weight: { + title: 'Weight', + type: 'string', + default: '500' + }, + color: { + title: 'color', + type: 'string', + default: null + } + } + }, + titleFont: { + title: 'Title text font', + type: 'object', + properties: { + family: { + title: 'Font family', + type: 'string', + default: 'Roboto' + }, + size: { + title: 'Size', + type: 'number', + default: 24 + }, + style: { + title: 'Style', + type: 'string', + default: 'normal' + }, + weight: { + title: 'Weight', + type: 'string', + default: '500' + }, + color: { + title: 'color', + type: 'string', + default: '#888' + } + } + }, + unitsFont: { + title: 'Units text font', + type: 'object', + properties: { + family: { + title: 'Font family', + type: 'string', + default: 'Roboto' + }, + size: { + title: 'Size', + type: 'number', + default: 22 + }, + style: { + title: 'Style', + type: 'string', + default: 'normal' + }, + weight: { + title: 'Weight', + type: 'string', + default: '500' + }, + color: { + title: 'color', + type: 'string', + default: '#888' + } + } + }, + valueFont: { + title: 'Value text font', + type: 'object', + properties: { + family: { + title: 'Font family', + type: 'string', + default: 'Roboto' + }, + size: { + title: 'Size', + type: 'number', + default: 40 + }, + style: { + title: 'Style', + type: 'string', + default: 'normal' + }, + weight: { + title: 'Weight', + type: 'string', + default: '500' + }, + color: { + title: 'color', + type: 'string', + default: '#444' + }, + shadowColor: { + title: 'Shadow color', + type: 'string', + default: 'rgba(0,0,0,0.3)' + } + } + }, + animation: { + title: 'Enable animation', + type: 'boolean', + default: true + }, + animationDuration: { + title: 'Animation duration', + type: 'number', + default: 500 + }, + animationRule: { + title: 'Animation rule', + type: 'string', + default: 'cycle' + } + }, + required: [] + }, + form: [ + 'minValue', + 'maxValue', + 'unitTitle', + 'showUnitTitle', + 'majorTicksCount', + 'minorTicks', + 'valueBox', + 'valueInt', + { + key: 'defaultColor', + type: 'color' + }, + { + key: 'colorPlate', + type: 'color' + }, + { + key: 'colorMajorTicks', + type: 'color' + }, + { + key: 'colorMinorTicks', + type: 'color' + }, + { + key: 'colorNeedle', + type: 'color' + }, + { + key: 'colorNeedleEnd', + type: 'color' + }, + { + key: 'colorNeedleShadowUp', + type: 'color' + }, + { + key: 'colorNeedleShadowDown', + type: 'color' + }, + { + key: 'colorValueBoxRect', + type: 'color' + }, + { + key: 'colorValueBoxRectEnd', + type: 'color' + }, + { + key: 'colorValueBoxBackground', + type: 'color' + }, + { + key: 'colorValueBoxShadow', + type: 'color' + }, + { + key: 'highlights', + items: [ + 'highlights[].from', + 'highlights[].to', + { + key: 'highlights[].color', + type: 'color' + } + ] + }, + 'highlightsWidth', + 'showBorder', + { + key: 'numbersFont', + items: [ + 'numbersFont.family', + 'numbersFont.size', + { + key: 'numbersFont.style', + type: 'rc-select', + multiple: false, + items: [ + { + value: 'normal', + label: 'Normal' + }, + { + value: 'italic', + label: 'Italic' + }, + { + value: 'oblique', + label: 'Oblique' + } + ] + }, + { + key: 'numbersFont.weight', + type: 'rc-select', + multiple: false, + items: [ + { + value: 'normal', + label: 'Normal' + }, + { + value: 'bold', + label: 'Bold' + }, + { + value: 'bolder', + label: 'Bolder' + }, + { + value: 'lighter', + label: 'Lighter' + }, + { + value: '100', + label: '100' + }, + { + value: '200', + label: '200' + }, + { + value: '300', + label: '300' + }, + { + value: '400', + label: '400' + }, + { + value: '500', + label: '500' + }, + { + value: '600', + label: '600' + }, + { + value: '700', + label: '800' + }, + { + value: '800', + label: '800' + }, + { + value: '900', + label: '900' + } + ] + }, + { + key: 'numbersFont.color', + type: 'color' + } + ] + }, + { + key: 'titleFont', + items: [ + 'titleFont.family', + 'titleFont.size', + { + key: 'titleFont.style', + type: 'rc-select', + multiple: false, + items: [ + { + value: 'normal', + label: 'Normal' + }, + { + value: 'italic', + label: 'Italic' + }, + { + value: 'oblique', + label: 'Oblique' + } + ] + }, + { + key: 'titleFont.weight', + type: 'rc-select', + multiple: false, + items: [ + { + value: 'normal', + label: 'Normal' + }, + { + value: 'bold', + label: 'Bold' + }, + { + value: 'bolder', + label: 'Bolder' + }, + { + value: 'lighter', + label: 'Lighter' + }, + { + value: '100', + label: '100' + }, + { + value: '200', + label: '200' + }, + { + value: '300', + label: '300' + }, + { + value: '400', + label: '400' + }, + { + value: '500', + label: '500' + }, + { + value: '600', + label: '600' + }, + { + value: '700', + label: '800' + }, + { + value: '800', + label: '800' + }, + { + value: '900', + label: '900' + } + ] + }, + { + key: 'titleFont.color', + type: 'color' + } + ] + }, + { + key: 'unitsFont', + items: [ + 'unitsFont.family', + 'unitsFont.size', + { + key: 'unitsFont.style', + type: 'rc-select', + multiple: false, + items: [ + { + value: 'normal', + label: 'Normal' + }, + { + value: 'italic', + label: 'Italic' + }, + { + value: 'oblique', + label: 'Oblique' + } + ] + }, + { + key: 'unitsFont.weight', + type: 'rc-select', + multiple: false, + items: [ + { + value: 'normal', + label: 'Normal' + }, + { + value: 'bold', + label: 'Bold' + }, + { + value: 'bolder', + label: 'Bolder' + }, + { + value: 'lighter', + label: 'Lighter' + }, + { + value: '100', + label: '100' + }, + { + value: '200', + label: '200' + }, + { + value: '300', + label: '300' + }, + { + value: '400', + label: '400' + }, + { + value: '500', + label: '500' + }, + { + value: '600', + label: '600' + }, + { + value: '700', + label: '800' + }, + { + value: '800', + label: '800' + }, + { + value: '900', + label: '900' + } + ] + }, + { + key: 'unitsFont.color', + type: 'color' + } + ] + }, + { + key: 'valueFont', + items: [ + 'valueFont.family', + 'valueFont.size', + { + key: 'valueFont.style', + type: 'rc-select', + multiple: false, + items: [ + { + value: 'normal', + label: 'Normal' + }, + { + value: 'italic', + label: 'Italic' + }, + { + value: 'oblique', + label: 'Oblique' + } + ] + }, + { + key: 'valueFont.weight', + type: 'rc-select', + multiple: false, + items: [ + { + value: 'normal', + label: 'Normal' + }, + { + value: 'bold', + label: 'Bold' + }, + { + value: 'bolder', + label: 'Bolder' + }, + { + value: 'lighter', + label: 'Lighter' + }, + { + value: '100', + label: '100' + }, + { + value: '200', + label: '200' + }, + { + value: '300', + label: '300' + }, + { + value: '400', + label: '400' + }, + { + value: '500', + label: '500' + }, + { + value: '600', + label: '600' + }, + { + value: '700', + label: '800' + }, + { + value: '800', + label: '800' + }, + { + value: '900', + label: '900' + } + ] + }, + { + key: 'valueFont.color', + type: 'color' + }, + { + key: 'valueFont.shadowColor', + type: 'color' + } + ] + }, + 'animation', + 'animationDuration', + { + key: 'animationRule', + type: 'rc-select', + multiple: false, + items: [ + { + value: 'linear', + label: 'Linear' + }, + { + value: 'quad', + label: 'Quad' + }, + { + value: 'quint', + label: 'Quint' + }, + { + value: 'cycle', + label: 'Cycle' + }, + { + value: 'bounce', + label: 'Bounce' + }, + { + value: 'elastic', + label: 'Elastic' + }, + { + value: 'dequad', + label: 'Dequad' + }, + { + value: 'dequint', + label: 'Dequint' + }, + { + value: 'decycle', + label: 'Decycle' + }, + { + value: 'debounce', + label: 'Debounce' + }, + { + value: 'delastic', + label: 'Delastic' + } + ] + } + ] +}; + +export abstract class TbBaseGauge { + + private gauge: BaseGauge; + + protected constructor(protected ctx: WidgetContext, canvasId: string) { + const gaugeElement = $('#'+canvasId, ctx.$container)[0]; + const settings: S = ctx.settings; + const gaugeData: O = this.createGaugeOptions(gaugeElement, settings); + this.gauge = this.createGauge(gaugeData as O).draw(); + } + + protected abstract createGaugeOptions(gaugeElement: HTMLElement, settings: S): O; + + protected abstract createGauge(gaugeData: O): BaseGauge; + + update() { + if (this.ctx.data.length > 0) { + const cellData = this.ctx.data[0]; + if (cellData.data.length > 0) { + const tvPair = cellData.data[cellData.data.length - + 1]; + const value = tvPair[1]; + if(value !== this.gauge.value) { + this.gauge.value = value; + } + } + } + } + + mobileModeChanged() { + const animation = this.ctx.settings.animation !== false && !this.ctx.isMobile; + this.gauge.update({animation} as GenericOptions); + } + + resize() { + this.gauge.update({width: this.ctx.width, height: this.ctx.height} as GenericOptions); + } +} + +export abstract class TbAnalogueGauge extends TbBaseGauge { + + protected constructor(ctx: WidgetContext, canvasId: string) { + super(ctx,canvasId); + } + + protected createGaugeOptions(gaugeElement: HTMLElement, settings: S): O { + + const minValue = settings.minValue || 0; + const maxValue = settings.maxValue || 100; + + const dataKey = this.ctx.data[0].dataKey; + const keyColor = settings.defaultColor || dataKey.color; + + const majorTicksCount = settings.majorTicksCount || 10; + const total = maxValue-minValue; + let step = (total/majorTicksCount); + + const valueInt = settings.valueInt || 3; + + const valueDec = getValueDec(this.ctx, settings); + + step = parseFloat(parseFloat(step+'').toFixed(valueDec)); + + const majorTicks: number[] = []; + const highlights: Highlight[] = []; + let tick = minValue; + + while(tick<=maxValue) { + majorTicks.push(tick); + let nextTick = tick+step; + nextTick = parseFloat(parseFloat(nextTick+'').toFixed(valueDec)); + if (tick 0 ? + settings.unitTitle : dataKey.label) : ''), + + borders: settings.showBorder !== false, + borderShadowWidth: (settings.showBorder !== false) ? 3 : 0, + + // number formats + valueInt, + valueDec, + majorTicksInt: 1, + majorTicksDec: 0, + + valueBox: settings.valueBox !== false, + valueBoxStroke: 5, + valueBoxWidth: 0, + valueText: '', + valueTextShadow: true, + valueBoxBorderRadius: 2.5, + + // highlights + highlights: (settings.highlights && settings.highlights.length > 0) ? settings.highlights : highlights, + highlightsWidth: (isDefined(settings.highlightsWidth) && settings.highlightsWidth !== null) ? settings.highlightsWidth : 15, + + // fonts + fontNumbers: getFontFamily(settings.numbersFont), + fontTitle: getFontFamily(settings.titleFont), + fontUnits: getFontFamily(settings.unitsFont), + fontValue: getFontFamily(settings.valueFont), + + fontNumbersSize: settings.numbersFont && settings.numbersFont.size ? settings.numbersFont.size : 18, + fontTitleSize: settings.titleFont && settings.titleFont.size ? settings.titleFont.size : 24, + fontUnitsSize: settings.unitsFont && settings.unitsFont.size ? settings.unitsFont.size : 22, + fontValueSize: settings.valueFont && settings.valueFont.size ? settings.valueFont.size : 40, + + fontNumbersStyle: settings.numbersFont && settings.numbersFont.style ? settings.numbersFont.style : 'normal', + fontTitleStyle: settings.titleFont && settings.titleFont.style ? settings.titleFont.style : 'normal', + fontUnitsStyle: settings.unitsFont && settings.unitsFont.style ? settings.unitsFont.style : 'normal', + fontValueStyle: settings.valueFont && settings.valueFont.style ? settings.valueFont.style : 'normal', + + fontNumbersWeight: settings.numbersFont && settings.numbersFont.weight ? settings.numbersFont.weight : '500', + fontTitleWeight: settings.titleFont && settings.titleFont.weight ? settings.titleFont.weight : '500', + fontUnitsWeight: settings.unitsFont && settings.unitsFont.weight ? settings.unitsFont.weight : '500', + fontValueWeight: settings.valueFont && settings.valueFont.weight ? settings.valueFont.weight : '500', + + colorNumbers: settings.numbersFont && settings.numbersFont.color ? settings.numbersFont.color : colorNumbers, + colorTitle: settings.titleFont && settings.titleFont.color ? settings.titleFont.color : '#888', + colorUnits: settings.unitsFont && settings.unitsFont.color ? settings.unitsFont.color : '#888', + colorValueText: settings.valueFont && settings.valueFont.color ? settings.valueFont.color : '#444', + colorValueTextShadow: settings.valueFont && settings.valueFont.shadowColor ? settings.valueFont.shadowColor : 'rgba(0,0,0,0.3)', + + // colors + colorPlate: settings.colorPlate || '#fff', + colorMajorTicks: settings.colorMajorTicks || '#444', + colorMinorTicks: settings.colorMinorTicks || '#666', + colorNeedle: settings.colorNeedle || keyColor, + colorNeedleEnd: settings.colorNeedleEnd || keyColor, + + colorValueBoxRect: settings.colorValueBoxRect || '#888', + colorValueBoxRectEnd: settings.colorValueBoxRectEnd || '#666', + colorValueBoxBackground: settings.colorValueBoxBackground || '#babab2', + colorValueBoxShadow: settings.colorValueBoxShadow || 'rgba(0,0,0,1)', + colorNeedleShadowUp: settings.colorNeedleShadowUp || 'rgba(2,255,255,0.2)', + colorNeedleShadowDown: settings.colorNeedleShadowDown || 'rgba(188,143,143,0.45)', + + // animations + animation: settings.animation !== false && !this.ctx.isMobile, + animationDuration: (isDefined(settings.animationDuration) && settings.animationDuration !== null) ? settings.animationDuration : 500, + animationRule: settings.animationRule || 'cycle' + } as O; + + this.prepareGaugeOptions(settings, gaugeData); + return gaugeData; + } + + protected abstract prepareGaugeOptions(settings: S, gaugeData: O); + +} + +function getValueDec(ctx: WidgetContext, settings: AnalogueGaugeSettings): number { + let dataKey; + if (ctx.data && ctx.data[0]) { + dataKey = ctx.data[0].dataKey; + } + if (dataKey && isDefined(dataKey.decimals)) { + return dataKey.decimals; + } else { + return (isDefined(settings.valueDec) && settings.valueDec !== null) + ? settings.valueDec : ctx.decimals; + } +} + +function getUnits(ctx: WidgetContext, settings: AnalogueGaugeSettings): string { + let dataKey; + if (ctx.data && ctx.data[0]) { + dataKey = ctx.data[0].dataKey; + } + if (dataKey && dataKey.units && dataKey.units.length) { + return dataKey.units; + } else { + return isDefined(settings.units) && settings.units.length > 0 ? settings.units : ctx.units; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/analogue-linear-gauge.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/analogue-linear-gauge.models.ts new file mode 100644 index 0000000000..1763798f35 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/analogue-linear-gauge.models.ts @@ -0,0 +1,88 @@ +/// +/// Copyright © 2016-2019 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { JsonSettingsSchema } from '@shared/models/widget.models'; +import { AnalogueGaugeSettings, analogueGaugeSettingsSchema } from '@home/components/widget/lib/analogue-gauge.models'; +import { deepClone } from '@core/utils'; + +export interface AnalogueLinearGaugeSettings extends AnalogueGaugeSettings { + barStrokeWidth: number; + colorBarStroke: string; + colorBar: string; + colorBarEnd: string; + colorBarProgress: string; + colorBarProgressEnd: string; +} + +export function getAnalogueLinearGaugeSettingsSchema(): JsonSettingsSchema { + const analogueLinearGaugeSettingsSchema = deepClone(analogueGaugeSettingsSchema); + analogueLinearGaugeSettingsSchema.schema.properties = + {...analogueLinearGaugeSettingsSchema.schema.properties, ...{ + barStrokeWidth: { + title: 'Bar stroke width', + type: 'number', + default: 2.5 + }, + colorBarStroke: { + title: 'Bar stroke color', + type: 'string', + default: null + }, + colorBar: { + title: 'Bar background color', + type: 'string', + default: '#fff' + }, + colorBarEnd: { + title: 'Bar background color - end gradient', + type: 'string', + default: '#ddd' + }, + colorBarProgress: { + title: 'Progress bar color', + type: 'string', + default: null + }, + colorBarProgressEnd: { + title: 'Progress bar color - end gradient', + type: 'string', + default: null + }}}; + analogueLinearGaugeSettingsSchema.form.unshift( + 'barStrokeWidth', + { + key: 'colorBarStroke', + type: 'color' + }, + { + key: 'colorBar', + type: 'color' + }, + { + key: 'colorBarEnd', + type: 'color' + }, + { + key: 'colorBarProgress', + type: 'color' + }, + { + key: 'colorBarProgressEnd', + type: 'color' + } + ); + return analogueLinearGaugeSettingsSchema; +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/analogue-linear-gauge.ts b/ui-ngx/src/app/modules/home/components/widget/lib/analogue-linear-gauge.ts new file mode 100644 index 0000000000..d46dcd799e --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/analogue-linear-gauge.ts @@ -0,0 +1,65 @@ +/// +/// Copyright © 2016-2019 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import * as CanvasGauges from 'canvas-gauges'; +import { JsonSettingsSchema } from '@shared/models/widget.models'; +import { WidgetContext } from '@home/models/widget-component.models'; +import { TbAnalogueGauge } from '@home/components/widget/lib/analogue-gauge.models'; +import { + AnalogueLinearGaugeSettings, + getAnalogueLinearGaugeSettingsSchema +} from '@home/components/widget/lib/analogue-linear-gauge.models'; +import { isDefined } from '@core/utils'; +import * as tinycolor_ from 'tinycolor2'; +import LinearGaugeOptions = CanvasGauges.LinearGaugeOptions; +import LinearGauge = CanvasGauges.LinearGauge; +import BaseGauge = CanvasGauges.BaseGauge; + +const tinycolor = tinycolor_; + +const analogueLinearGaugeSettingsSchemaValue = getAnalogueLinearGaugeSettingsSchema(); + +export class TbAnalogueLinearGauge extends TbAnalogueGauge{ + + static get settingsSchema(): JsonSettingsSchema { + return analogueLinearGaugeSettingsSchemaValue; + } + + constructor(ctx: WidgetContext, canvasId: string) { + super(ctx, canvasId); + } + + protected prepareGaugeOptions(settings: AnalogueLinearGaugeSettings, gaugeData: LinearGaugeOptions) { + const dataKey = this.ctx.data[0].dataKey; + const keyColor = settings.defaultColor || dataKey.color; + + const barStrokeColor = tinycolor(keyColor).darken().setAlpha(0.6).toRgbString(); + const progressColorStart = tinycolor(keyColor).setAlpha(0.05).toRgbString(); + const progressColorEnd = tinycolor(keyColor).darken().toRgbString(); + + gaugeData.barStrokeWidth = (isDefined(settings.barStrokeWidth) && settings.barStrokeWidth !== null) ? settings.barStrokeWidth : 2.5; + gaugeData.colorBarStroke = settings.colorBarStroke || barStrokeColor; + gaugeData.colorBar = settings.colorBar || '#fff'; + gaugeData.colorBarEnd = settings.colorBarEnd || '#ddd'; + gaugeData.colorBarProgress = settings.colorBarProgress || progressColorStart; + gaugeData.colorBarProgressEnd = settings.colorBarProgressEnd || progressColorEnd; + } + + protected createGauge(gaugeData: LinearGaugeOptions): BaseGauge { + return new LinearGauge(gaugeData); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/analogue-radial-gauge.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/analogue-radial-gauge.models.ts new file mode 100644 index 0000000000..c6ff9a0ceb --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/analogue-radial-gauge.models.ts @@ -0,0 +1,52 @@ +/// +/// Copyright © 2016-2019 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { JsonSettingsSchema } from '@shared/models/widget.models'; +import { AnalogueGaugeSettings, analogueGaugeSettingsSchema } from '@home/components/widget/lib/analogue-gauge.models'; +import { deepClone } from '@core/utils'; + +export interface AnalogueRadialGaugeSettings extends AnalogueGaugeSettings { + startAngle: number; + ticksAngle: number; + needleCircleSize: number; +} + +export function getAnalogueRadialGaugeSettingsSchema(): JsonSettingsSchema { + const analogueRadialGaugeSettingsSchema = deepClone(analogueGaugeSettingsSchema); + analogueRadialGaugeSettingsSchema.schema.properties = + {...analogueRadialGaugeSettingsSchema.schema.properties, ...{ + startAngle: { + title: 'Start ticks angle', + type: 'number', + default: 45 + }, + ticksAngle: { + title: 'Ticks angle', + type: 'number', + default: 270 + }, + needleCircleSize: { + title: 'Needle circle size', + type: 'number', + default: 10 + }}}; + analogueRadialGaugeSettingsSchema.form.unshift( + 'startAngle', + 'ticksAngle', + 'needleCircleSize' + ); + return analogueRadialGaugeSettingsSchema; +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/analogue-radial-gauge.ts b/ui-ngx/src/app/modules/home/components/widget/lib/analogue-radial-gauge.ts new file mode 100644 index 0000000000..81f9cc59dc --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/analogue-radial-gauge.ts @@ -0,0 +1,64 @@ +/// +/// Copyright © 2016-2019 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import * as CanvasGauges from 'canvas-gauges'; +import { + AnalogueRadialGaugeSettings, getAnalogueRadialGaugeSettingsSchema +} from '@home/components/widget/lib/analogue-radial-gauge.models'; +import { JsonSettingsSchema } from '@shared/models/widget.models'; +import { WidgetContext } from '@home/models/widget-component.models'; +import { TbAnalogueGauge } from '@home/components/widget/lib/analogue-gauge.models'; +import RadialGauge = CanvasGauges.RadialGauge; +import RadialGaugeOptions = CanvasGauges.RadialGaugeOptions; +import BaseGauge = CanvasGauges.BaseGauge; + +const analogueRadialGaugeSettingsSchemaValue = getAnalogueRadialGaugeSettingsSchema(); + +export class TbAnalogueRadialGauge extends TbAnalogueGauge{ + + static get settingsSchema(): JsonSettingsSchema { + return analogueRadialGaugeSettingsSchemaValue; + } + + constructor(ctx: WidgetContext, canvasId: string) { + super(ctx, canvasId); + } + + protected prepareGaugeOptions(settings: AnalogueRadialGaugeSettings, gaugeData: RadialGaugeOptions) { + gaugeData.ticksAngle = settings.ticksAngle || 270; + gaugeData.startAngle = settings.startAngle || 45; + + // colors + + gaugeData.colorNeedleCircleOuter = '#f0f0f0'; + gaugeData.colorNeedleCircleOuterEnd = '#ccc'; + gaugeData.colorNeedleCircleInner = '#e8e8e8'; // tinycolor(keyColor).lighten(30).toRgbString(),//'#e8e8e8', + gaugeData.colorNeedleCircleInnerEnd = '#f5f5f5'; + + // needle + gaugeData.needleCircleSize = settings.needleCircleSize || 10; + gaugeData.needleCircleInner = true; + gaugeData.needleCircleOuter = true; + + // custom animations + gaugeData.animationTarget = 'needle'; // 'needle' or 'plate' + } + + protected createGauge(gaugeData: RadialGaugeOptions): BaseGauge { + return new RadialGauge(gaugeData); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings.models.ts new file mode 100644 index 0000000000..c66495da7d --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings.models.ts @@ -0,0 +1,38 @@ +/// +/// Copyright © 2016-2019 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + + +export type FontStyle = 'normal' | 'italic' | 'oblique'; +export type FontWeight = 'normal' | 'bold' | 'bolder' | 'lighter' + | '100' | '200' | '300' | '400' | '500' + | '600' | '700' | '800' | '900'; + +export interface FontSettings { + family: string; + size: number; + style: FontStyle; + weight: FontWeight; + color: string; + shadowColor?: string; +} + +export function getFontFamily(fontSettings: FontSettings): string { + let family = fontSettings && fontSettings.family ? fontSettings.family : 'Roboto'; + if (family === 'RobotoDraft') { + family = 'Roboto'; + } + return family; +} diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts b/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts index e0321d0b86..01614c8bb5 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts @@ -37,17 +37,11 @@ import { DynamicWidgetComponent } from '@home/components/widget/dynamic-widget.c import { WidgetComponentsModule } from '@home/components/widget/widget-components.module'; import { WINDOW } from '@core/services/window.service'; -import * as tinycolor_ from 'tinycolor2'; -import { TbFlot } from './lib/flot-widget'; import { NULL_UUID } from '@shared/models/id/has-uuid'; import { WidgetTypeId } from '@app/shared/models/id/widget-type-id'; import { TenantId } from '@app/shared/models/id/tenant-id'; import { SharedModule } from '@shared/shared.module'; -const tinycolor = tinycolor_; - -// declare var jQuery: any; - // @dynamic @Injectable() export class WidgetComponentService { @@ -70,12 +64,6 @@ export class WidgetComponentService { private utils: UtilsService, private resources: ResourcesService, private translate: TranslateService) { - // @ts-ignore - this.window.tinycolor = tinycolor; - // @ts-ignore - this.window.cssjs = cssjs; - // @ts-ignore - this.window.TbFlot = TbFlot; this.cssParser.testMode = false; diff --git a/ui-ngx/src/app/shared/components/json-form/json-form.component.ts b/ui-ngx/src/app/shared/components/json-form/json-form.component.ts index e3b0dcd7e1..54f26f694d 100644 --- a/ui-ngx/src/app/shared/components/json-form/json-form.component.ts +++ b/ui-ngx/src/app/shared/components/json-form/json-form.component.ts @@ -168,11 +168,11 @@ export class JsonFormComponent implements OnInit, ControlValueAccessor, Validato } } - private onModelChange(key: (string | number)[], val: any) { + private onModelChange(key: (string | number)[], val: any, forceUpdate = false) { if (isString(val) && val === '') { val = undefined; } - if (JsonFormUtils.updateValue(key, this.model, val)) { + if (JsonFormUtils.updateValue(key, this.model, val) || forceUpdate) { this.formProps.model = this.model; this.isModelValid = this.validateModel(); this.updateView(); diff --git a/ui-ngx/src/app/shared/components/json-form/react/json-form-array.tsx b/ui-ngx/src/app/shared/components/json-form/react/json-form-array.tsx index 5aba36cc98..7cdc3cf9c7 100644 --- a/ui-ngx/src/app/shared/components/json-form/react/json-form-array.tsx +++ b/ui-ngx/src/app/shared/components/json-form/react/json-form-array.tsx @@ -96,7 +96,7 @@ class ThingsboardArray extends React.Component class

}*/ } - onChangeValidate(e) { + onChangeValidate(e, forceUpdate?: boolean) { let value = null; if (this.props.form.schema.type === 'integer' || this.props.form.schema.type === 'number') { if (e.target.value === null || e.target.value === '') { @@ -61,7 +61,7 @@ export default ThingsboardBaseComponent => class

valid: validationResult.valid, error: validationResult.valid ? null : validationResult.error.message }); - this.props.onChange(this.props.form.key, value); + this.props.onChange(this.props.form.key, value, forceUpdate); } defaultValue() { diff --git a/ui-ngx/src/app/shared/components/json-form/react/json-form-radios.tsx b/ui-ngx/src/app/shared/components/json-form/react/json-form-radios.tsx index 96535e1208..85f884d331 100644 --- a/ui-ngx/src/app/shared/components/json-form/react/json-form-radios.tsx +++ b/ui-ngx/src/app/shared/components/json-form/react/json-form-radios.tsx @@ -6,7 +6,6 @@ import FormControl from '@material-ui/core/FormControl'; import ThingsboardBaseComponent from '@shared/components/json-form/react/json-form-base-component'; class ThingsboardRadios extends React.Component { - render() { const items = this.props.form.titleMap.map((item, index) => { return ( @@ -19,7 +18,9 @@ class ThingsboardRadios extends React.Component {this.props.form.title} - + { + this.props.onChangeValidate(e); + }}> {items} diff --git a/ui-ngx/src/app/shared/components/json-form/react/json-form-schema-form.tsx b/ui-ngx/src/app/shared/components/json-form/react/json-form-schema-form.tsx index 153a9b0d7e..fbb8fee7db 100644 --- a/ui-ngx/src/app/shared/components/json-form/react/json-form-schema-form.tsx +++ b/ui-ngx/src/app/shared/components/json-form/react/json-form-schema-form.tsx @@ -73,8 +73,8 @@ class ThingsboardSchemaForm extends React.Component { this.hasConditions = false; } - onChange(key: (string | number)[], val: any) { - this.props.onModelChange(key, val); + onChange(key: (string | number)[], val: any, forceUpdate?: boolean) { + this.props.onModelChange(key, val, forceUpdate); if (this.hasConditions) { this.forceUpdate(); } diff --git a/ui-ngx/src/app/shared/components/json-form/react/json-form-text.tsx b/ui-ngx/src/app/shared/components/json-form/react/json-form-text.tsx index 44186cbd47..c7045d759f 100644 --- a/ui-ngx/src/app/shared/components/json-form/react/json-form-text.tsx +++ b/ui-ngx/src/app/shared/components/json-form/react/json-form-text.tsx @@ -68,7 +68,9 @@ class ThingsboardText extends React.Component { + this.props.onChangeValidate(e); + }} defaultValue={this.props.value} disabled={this.props.form.readonly} rows={rows} diff --git a/ui-ngx/src/app/shared/components/json-form/react/json-form.models.ts b/ui-ngx/src/app/shared/components/json-form/react/json-form.models.ts index 7e3c466e24..d30160b2b0 100644 --- a/ui-ngx/src/app/shared/components/json-form/react/json-form.models.ts +++ b/ui-ngx/src/app/shared/components/json-form/react/json-form.models.ts @@ -50,7 +50,7 @@ export interface GroupInfo { GroupTitle: string; } -export type onChangeFn = (key: (string | number)[], val: any) => void; +export type onChangeFn = (key: (string | number)[], val: any, forceUpdate?: boolean) => void; export type OnColorClickFn = (key: (string | number)[], val: tinycolor.ColorFormats.RGBA, colorSelectedFn: (color: tinycolor.ColorFormats.RGBA) => void) => void; export type onToggleFullscreenFn = (element: HTMLElement, fullscreenFinishFn?: () => void) => void; @@ -122,7 +122,7 @@ export interface JsonFormFieldProps { mapper?: {[type: string]: any}; onChange?: onChangeFn; onColorClick?: OnColorClickFn; - onChangeValidate?: (e: any) => void; + onChangeValidate?: (e: any, forceUpdate?: boolean) => void; onToggleFullscreen?: onToggleFullscreenFn; valid?: boolean; error?: string; diff --git a/ui-ngx/src/polyfills.ts b/ui-ngx/src/polyfills.ts index f6abc58770..cab9d5fc94 100644 --- a/ui-ngx/src/polyfills.ts +++ b/ui-ngx/src/polyfills.ts @@ -81,3 +81,24 @@ import 'core-js/es/array'; */ (window as any).global = window; + +/*************************************************************************************************** + * WIDGETS IMPORTS + */ + +import cssjs from '@core/css/css'; +import { TbFlot } from '@home/components/widget/lib/flot-widget'; +import { TbAnalogueCompass } from '@home/components/widget/lib/analogue-compass'; +import { TbAnalogueRadialGauge } from '@home/components/widget/lib/analogue-radial-gauge'; +import { TbAnalogueLinearGauge } from '@home/components/widget/lib/analogue-linear-gauge'; + +import * as tinycolor_ from 'tinycolor2'; + +const tinycolor = tinycolor_; + +(window as any).tinycolor = tinycolor; +(window as any).cssjs = cssjs; +(window as any).TbFlot = TbFlot; +(window as any).TbAnalogueCompass = TbAnalogueCompass; +(window as any).TbAnalogueRadialGauge = TbAnalogueRadialGauge; +(window as any).TbAnalogueLinearGauge = TbAnalogueLinearGauge; diff --git a/ui-ngx/src/scss/fonts.scss b/ui-ngx/src/scss/fonts.scss new file mode 100644 index 0000000000..b6eaf1c091 --- /dev/null +++ b/ui-ngx/src/scss/fonts.scss @@ -0,0 +1,21 @@ +/** + * Copyright © 2016-2019 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@font-face { + font-family: "Segment7Standard"; + font-style: italic; + font-weight: 400; + src: url("data:font/opentype;charset=utf-8;base64,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AAEAAAAIAAAAAAAEAAAAAAAAAAEAAAAAzD2izwAAAADPr89TAAAAAM+4fiECSQAAAkkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABpwBCAC8ALwAvAFwBpwAvAC8ALwBcAC8AXAIBAC8ALwGWAC8ALwBCAC4ALgBYAC8ALwBcAacALwAvAC8ALwAvAC8ALwAvAC8ALwAvAC8ALwGWAC8ALwAvAC8ALwAvAC8AQgAvAC4ALwAvAC8ALwAvAC8ALwAvAEIALwBCAFwBpwAvAC8ALwAvAC8ALwAvAC8BlgAvAC8ALwAvAC8ALwAvAEIALwAuAC8ALwAvAC8ALwAvAC8AAAGWAAAALwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEIAAAAAAAAAAAAvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC8ALwAAAAAAAAAAAAAAAAAAAC8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA") format("opentype"); +} diff --git a/ui-ngx/src/styles.scss b/ui-ngx/src/styles.scss index 17227f2d5b..6cae1030aa 100644 --- a/ui-ngx/src/styles.scss +++ b/ui-ngx/src/styles.scss @@ -22,6 +22,7 @@ @import './scss/constants'; @import './scss/animations'; @import './scss/mixins'; +@import './scss/fonts'; body, html { height: 100%; diff --git a/ui-ngx/src/tsconfig.app.json b/ui-ngx/src/tsconfig.app.json index caff807fa5..9ec8b0ebd8 100644 --- a/ui-ngx/src/tsconfig.app.json +++ b/ui-ngx/src/tsconfig.app.json @@ -2,7 +2,7 @@ "extends": "../tsconfig.json", "compilerOptions": { "outDir": "../out-tsc/app", - "types": ["node", "jquery", "flot", "tooltipster", "tinycolor2", "js-beautify", "react", "react-dom", "jstree", "raphael"] + "types": ["node", "jquery", "flot", "tooltipster", "tinycolor2", "js-beautify", "react", "react-dom", "jstree", "raphael", "canvas-gauges"] }, "angularCompilerOptions": { "fullTemplateTypeCheck": true