Commit d18ca19b3bd205549417e948e6c56cb55e4a6870

Authored by Igor Kulikov
1 parent c860753b

UI: New widget type 'HTML Card'

... ... @@ -83,6 +83,11 @@ VALUES ( now ( ), minTimeuuid ( 0 ), 'cards', 'timeseries_table',
83 83 'Timeseries table' );
84 84
85 85 INSERT INTO "thingsboard"."widget_type" ( "id", "tenant_id", "bundle_alias", "alias", "descriptor", "name" )
  86 +VALUES ( now ( ), minTimeuuid ( 0 ), 'cards', 'html_card',
  87 +'{"type":"static","sizeX":7.5,"sizeY":3,"resources":[],"templateHtml":"","templateCss":"","controllerScript":"fns.init = function(containerElement, settings, datasources,\n data) {\n\n var container = $(containerElement);\n \n var cssParser = new cssjs();\n cssParser.testMode = false;\n var namespace = ''html-card-'' + hashCode(settings.cardCss);\n cssParser.cssPreviewNamespace = namespace;\n cssParser.createStyleElement(namespace, settings.cardCss);\n container.addClass(namespace);\n container.html(settings.cardHtml);\n \n function hashCode(str) {\n var hash = 0;\n var i, char;\n if (str.length === 0) return hash;\n for (i = 0; i < str.length; i++) {\n char = str.charCodeAt(i);\n hash = ((hash << 5) - hash) + char;\n hash = hash & hash;\n }\n return hash;\n }\n\n}\n\n\nfns.redraw = function(containerElement, width, height, data, timeWindow, sizeChanged) {\n\n};","settingsSchema":"{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"required\": [\"cardHtml\"],\n \"properties\": {\n \"cardCss\": {\n \"title\": \"CSS\",\n \"type\": \"string\",\n \"default\": \".card {\\n font-weight: bold; \\n}\"\n },\n \"cardHtml\": {\n \"title\": \"HTML\",\n \"type\": \"string\",\n \"default\": \"<div class=''card''>HTML code here</div>\"\n }\n }\n },\n \"form\": [\n {\n \"key\": \"cardCss\",\n \"type\": \"css\"\n }, \n {\n \"key\": \"cardHtml\",\n \"type\": \"html\"\n } \n ]\n}","dataKeySettingsSchema":"{}\n","defaultConfig":"{\"datasources\":[{\"type\":\"static\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"cardHtml\":\"<div class=''card''>HTML code here</div>\",\"cardCss\":\".card {\\n font-weight: bold;\\n font-size: 32px;\\n color: #999;\\n width: 100%;\\n height: 100%;\\n display: flex;\\n align-items: center;\\n justify-content: center;\\n}\"},\"title\":\"HTML Card\",\"dropShadow\":true}"}',
  88 +'HTML Card' );
  89 +
  90 +INSERT INTO "thingsboard"."widget_type" ( "id", "tenant_id", "bundle_alias", "alias", "descriptor", "name" )
86 91 VALUES ( now ( ), minTimeuuid ( 0 ), 'analogue_gauges', 'speed_gauge_canvas_gauges',
87 92 '{"type":"latest","sizeX":7,"sizeY":5,"resources":[],"templateHtml":"<canvas id=\"radialGauge\"></canvas>\n","templateCss":"","controllerScript":"var gauge;\n\nfns.init = function(containerElement, settings, datasources,\n data) {\n gauge = new TbAnalogueRadialGauge(containerElement, settings, data, ''radialGauge''); \n\n}\n\n\nfns.redraw = function(containerElement, width, height, data, timeWindow, sizeChanged) {\n gauge.redraw(width, height, data, sizeChanged);\n};\n\nfns.destroy = function() {\n}\n","settingsSchema":"{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"minValue\": {\n \"title\": \"Minimum value\",\n \"type\": \"number\",\n \"default\": 0\n },\n \"maxValue\": {\n \"title\": \"Maximum value\",\n \"type\": \"number\",\n \"default\": 100\n },\n \"unitTitle\": {\n \"title\": \"Unit title\",\n \"type\": \"string\",\n \"default\": null\n },\n \"showUnitTitle\": {\n \"title\": \"Show unit title\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"units\": {\n \"title\": \"Units\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"majorTicksCount\": {\n \"title\": \"Major ticks count\",\n \"type\": \"number\",\n \"default\": null\n },\n \"minorTicks\": {\n \"title\": \"Minor ticks count\",\n \"type\": \"number\",\n \"default\": 2\n },\n \"valueBox\": {\n \"title\": \"Show value box\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"valueInt\": {\n \"title\": \"Digits count for integer part of value\",\n \"type\": \"number\",\n \"default\": 3\n },\n \"valueDec\": {\n \"title\": \"Digits count for decimal part of value\",\n \"type\": \"number\",\n \"default\": 2\n },\n \"defaultColor\": {\n \"title\": \"Default color\",\n \"type\": \"string\",\n \"default\": null\n },\n \"colorPlate\": {\n \"title\": \"Plate color\",\n \"type\": \"string\",\n \"default\": \"#fff\"\n },\n \"colorMajorTicks\": {\n \"title\": \"Major ticks color\",\n \"type\": \"string\",\n \"default\": \"#444\"\n },\n \"colorMinorTicks\": {\n \"title\": \"Minor ticks color\",\n \"type\": \"string\",\n \"default\": \"#666\"\n },\n \"colorNeedle\": {\n \"title\": \"Needle color\",\n \"type\": \"string\",\n \"default\": null\n },\n \"colorNeedleEnd\": {\n \"title\": \"Needle color - end gradient\",\n \"type\": \"string\",\n \"default\": null\n },\n \"colorNeedleShadowUp\": {\n \"title\": \"Upper half of the needle shadow color\",\n \"type\": \"string\",\n \"default\": \"rgba(2,255,255,0.2)\"\n },\n \"colorNeedleShadowDown\": {\n \"title\": \"Drop shadow needle color.\",\n \"type\": \"string\",\n \"default\": \"rgba(188,143,143,0.45)\"\n },\n \"colorValueBoxRect\": {\n \"title\": \"Value box rectangle stroke color\",\n \"type\": \"string\",\n \"default\": \"#888\"\n },\n \"colorValueBoxRectEnd\": {\n \"title\": \"Value box rectangle stroke color - end gradient\",\n \"type\": \"string\",\n \"default\": \"#666\"\n },\n \"colorValueBoxBackground\": {\n \"title\": \"Value box background color\",\n \"type\": \"string\",\n \"default\": \"#babab2\"\n },\n \"colorValueBoxShadow\": {\n \"title\": \"Value box shadow color\",\n \"type\": \"string\",\n \"default\": \"rgba(0,0,0,1)\"\n },\n \"highlights\": {\n \"title\": \"Highlights\",\n \"type\": \"array\",\n \"items\": {\n \"title\": \"Highlight\",\n \"type\": \"object\",\n \"properties\": {\n \"from\": {\n \"title\": \"From\",\n \"type\": \"number\"\n },\n \"to\": {\n \"title\": \"To\",\n \"type\": \"number\"\n },\n \"color\": {\n \"title\": \"Color\",\n \"type\": \"string\"\n }\n }\n }\n },\n \"highlightsWidth\": {\n \"title\": \"Highlights width\",\n \"type\": \"number\",\n \"default\": 15\n },\n \"showBorder\": {\n \"title\": \"Show border\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"numbersFont\": {\n \"title\": \"Tick numbers font\",\n \"type\": \"object\",\n \"properties\": {\n \"family\": {\n \"title\": \"Font family\",\n \"type\": \"string\",\n \"default\": \"RobotoDraft\"\n },\n \"size\": {\n \"title\": \"Size\",\n \"type\": \"number\",\n \"default\": 18\n },\n \"style\": {\n \"title\": \"Style\",\n \"type\": \"string\",\n \"default\": \"normal\"\n },\n \"weight\": {\n \"title\": \"Weight\",\n \"type\": \"string\",\n \"default\": \"500\"\n },\n \"color\": {\n \"title\": \"color\",\n \"type\": \"string\",\n \"default\": null\n }\n }\n },\n \"titleFont\": {\n \"title\": \"Title text font\",\n \"type\": \"object\",\n \"properties\": {\n \"family\": {\n \"title\": \"Font family\",\n \"type\": \"string\",\n \"default\": \"RobotoDraft\"\n },\n \"size\": {\n \"title\": \"Size\",\n \"type\": \"number\",\n \"default\": 24\n },\n \"style\": {\n \"title\": \"Style\",\n \"type\": \"string\",\n \"default\": \"normal\"\n },\n \"weight\": {\n \"title\": \"Weight\",\n \"type\": \"string\",\n \"default\": \"500\"\n },\n \"color\": {\n \"title\": \"color\",\n \"type\": \"string\",\n \"default\": \"#888\"\n }\n }\n },\n \"unitsFont\": {\n \"title\": \"Units text font\",\n \"type\": \"object\",\n \"properties\": {\n \"family\": {\n \"title\": \"Font family\",\n \"type\": \"string\",\n \"default\": \"RobotoDraft\"\n },\n \"size\": {\n \"title\": \"Size\",\n \"type\": \"number\",\n \"default\": 22\n },\n \"style\": {\n \"title\": \"Style\",\n \"type\": \"string\",\n \"default\": \"normal\"\n },\n \"weight\": {\n \"title\": \"Weight\",\n \"type\": \"string\",\n \"default\": \"500\"\n },\n \"color\": {\n \"title\": \"color\",\n \"type\": \"string\",\n \"default\": \"#888\"\n }\n }\n },\n \"valueFont\": {\n \"title\": \"Value text font\",\n \"type\": \"object\",\n \"properties\": {\n \"family\": {\n \"title\": \"Font family\",\n \"type\": \"string\",\n \"default\": \"RobotoDraft\"\n },\n \"size\": {\n \"title\": \"Size\",\n \"type\": \"number\",\n \"default\": 40\n },\n \"style\": {\n \"title\": \"Style\",\n \"type\": \"string\",\n \"default\": \"normal\"\n },\n \"weight\": {\n \"title\": \"Weight\",\n \"type\": \"string\",\n \"default\": \"500\"\n },\n \"color\": {\n \"title\": \"color\",\n \"type\": \"string\",\n \"default\": \"#444\"\n },\n \"shadowColor\": {\n \"title\": \"Shadow color\",\n \"type\": \"string\",\n \"default\": \"rgba(0,0,0,0.3)\"\n }\n }\n },\n \"animation\": {\n \"title\": \"Enable animation\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"animationDuration\": {\n \"title\": \"Animation duration\",\n \"type\": \"number\",\n \"default\": 500\n },\n \"animationRule\": {\n \"title\": \"Animation rule\",\n \"type\": \"string\",\n \"default\": \"cycle\"\n },\n \"startAngle\": {\n \"title\": \"Start ticks angle\",\n \"type\": \"number\",\n \"default\": 45\n },\n \"ticksAngle\": {\n \"title\": \"Ticks angle\",\n \"type\": \"number\",\n \"default\": 270\n },\n \"needleCircleSize\": {\n \"title\": \"Needle circle size\",\n \"type\": \"number\",\n \"default\": 10\n }\n },\n \"required\": []\n },\n \"form\": [\n \"startAngle\",\n \"ticksAngle\",\n \"needleCircleSize\",\n \"minValue\",\n \"maxValue\",\n \"unitTitle\",\n \"showUnitTitle\",\n \"units\",\n \"majorTicksCount\",\n \"minorTicks\",\n \"valueBox\",\n \"valueInt\",\n \"valueDec\",\n {\n \"key\": \"defaultColor\",\n \"type\": \"color\"\n },\n {\n \"key\": \"colorPlate\",\n \"type\": \"color\"\n },\n {\n \"key\": \"colorMajorTicks\",\n \"type\": \"color\"\n },\n {\n \"key\": \"colorMinorTicks\",\n \"type\": \"color\"\n },\n {\n \"key\": \"colorNeedle\",\n \"type\": \"color\"\n },\n {\n \"key\": \"colorNeedleEnd\",\n \"type\": \"color\"\n },\n {\n \"key\": \"colorNeedleShadowUp\",\n \"type\": \"color\"\n },\n {\n \"key\": \"colorNeedleShadowDown\",\n \"type\": \"color\"\n },\n {\n \"key\": \"colorValueBoxRect\",\n \"type\": \"color\"\n },\n {\n \"key\": \"colorValueBoxRectEnd\",\n \"type\": \"color\"\n },\n {\n \"key\": \"colorValueBoxBackground\",\n \"type\": \"color\"\n },\n {\n \"key\": \"colorValueBoxShadow\",\n \"type\": \"color\"\n },\n {\n \"key\": \"highlights\",\n \"items\": [\n \"highlights[].from\",\n \"highlights[].to\",\n {\n \"key\": \"highlights[].color\",\n \"type\": \"color\"\n }\n ]\n },\n \"highlightsWidth\",\n \"showBorder\",\n {\n \"key\": \"numbersFont\",\n \"items\": [\n \"numbersFont.family\",\n \"numbersFont.size\",\n {\n \"key\": \"numbersFont.style\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"normal\",\n \"label\": \"Normal\"\n },\n {\n \"value\": \"italic\",\n \"label\": \"Italic\"\n },\n {\n \"value\": \"oblique\",\n \"label\": \"Oblique\"\n }\n ]\n },\n {\n \"key\": \"numbersFont.weight\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"normal\",\n \"label\": \"Normal\"\n },\n {\n \"value\": \"bold\",\n \"label\": \"Bold\"\n },\n {\n \"value\": \"bolder\",\n \"label\": \"Bolder\"\n },\n {\n \"value\": \"lighter\",\n \"label\": \"Lighter\"\n },\n {\n \"value\": \"100\",\n \"label\": \"100\"\n },\n {\n \"value\": \"200\",\n \"label\": \"200\"\n },\n {\n \"value\": \"300\",\n \"label\": \"300\"\n },\n {\n \"value\": \"400\",\n \"label\": \"400\"\n },\n {\n \"value\": \"500\",\n \"label\": \"500\"\n },\n {\n \"value\": \"600\",\n \"label\": \"600\"\n },\n {\n \"value\": \"700\",\n \"label\": \"800\"\n },\n {\n \"value\": \"800\",\n \"label\": \"800\"\n },\n {\n \"value\": \"900\",\n \"label\": \"900\"\n }\n ]\n },\n {\n \"key\": \"numbersFont.color\",\n \"type\": \"color\"\n }\n ]\n },\n {\n \"key\": \"titleFont\",\n \"items\": [\n \"titleFont.family\",\n \"titleFont.size\",\n {\n \"key\": \"titleFont.style\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"normal\",\n \"label\": \"Normal\"\n },\n {\n \"value\": \"italic\",\n \"label\": \"Italic\"\n },\n {\n \"value\": \"oblique\",\n \"label\": \"Oblique\"\n }\n ]\n },\n {\n \"key\": \"titleFont.weight\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"normal\",\n \"label\": \"Normal\"\n },\n {\n \"value\": \"bold\",\n \"label\": \"Bold\"\n },\n {\n \"value\": \"bolder\",\n \"label\": \"Bolder\"\n },\n {\n \"value\": \"lighter\",\n \"label\": \"Lighter\"\n },\n {\n \"value\": \"100\",\n \"label\": \"100\"\n },\n {\n \"value\": \"200\",\n \"label\": \"200\"\n },\n {\n \"value\": \"300\",\n \"label\": \"300\"\n },\n {\n \"value\": \"400\",\n \"label\": \"400\"\n },\n {\n \"value\": \"500\",\n \"label\": \"500\"\n },\n {\n \"value\": \"600\",\n \"label\": \"600\"\n },\n {\n \"value\": \"700\",\n \"label\": \"800\"\n },\n {\n \"value\": \"800\",\n \"label\": \"800\"\n },\n {\n \"value\": \"900\",\n \"label\": \"900\"\n }\n ]\n },\n {\n \"key\": \"titleFont.color\",\n \"type\": \"color\"\n }\n ]\n },\n {\n \"key\": \"unitsFont\",\n \"items\": [\n \"unitsFont.family\",\n \"unitsFont.size\",\n {\n \"key\": \"unitsFont.style\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"normal\",\n \"label\": \"Normal\"\n },\n {\n \"value\": \"italic\",\n \"label\": \"Italic\"\n },\n {\n \"value\": \"oblique\",\n \"label\": \"Oblique\"\n }\n ]\n },\n {\n \"key\": \"unitsFont.weight\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"normal\",\n \"label\": \"Normal\"\n },\n {\n \"value\": \"bold\",\n \"label\": \"Bold\"\n },\n {\n \"value\": \"bolder\",\n \"label\": \"Bolder\"\n },\n {\n \"value\": \"lighter\",\n \"label\": \"Lighter\"\n },\n {\n \"value\": \"100\",\n \"label\": \"100\"\n },\n {\n \"value\": \"200\",\n \"label\": \"200\"\n },\n {\n \"value\": \"300\",\n \"label\": \"300\"\n },\n {\n \"value\": \"400\",\n \"label\": \"400\"\n },\n {\n \"value\": \"500\",\n \"label\": \"500\"\n },\n {\n \"value\": \"600\",\n \"label\": \"600\"\n },\n {\n \"value\": \"700\",\n \"label\": \"800\"\n },\n {\n \"value\": \"800\",\n \"label\": \"800\"\n },\n {\n \"value\": \"900\",\n \"label\": \"900\"\n }\n ]\n },\n {\n \"key\": \"unitsFont.color\",\n \"type\": \"color\"\n }\n ]\n },\n {\n \"key\": \"valueFont\",\n \"items\": [\n \"valueFont.family\",\n \"valueFont.size\",\n {\n \"key\": \"valueFont.style\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"normal\",\n \"label\": \"Normal\"\n },\n {\n \"value\": \"italic\",\n \"label\": \"Italic\"\n },\n {\n \"value\": \"oblique\",\n \"label\": \"Oblique\"\n }\n ]\n },\n {\n \"key\": \"valueFont.weight\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"normal\",\n \"label\": \"Normal\"\n },\n {\n \"value\": \"bold\",\n \"label\": \"Bold\"\n },\n {\n \"value\": \"bolder\",\n \"label\": \"Bolder\"\n },\n {\n \"value\": \"lighter\",\n \"label\": \"Lighter\"\n },\n {\n \"value\": \"100\",\n \"label\": \"100\"\n },\n {\n \"value\": \"200\",\n \"label\": \"200\"\n },\n {\n \"value\": \"300\",\n \"label\": \"300\"\n },\n {\n \"value\": \"400\",\n \"label\": \"400\"\n },\n {\n \"value\": \"500\",\n \"label\": \"500\"\n },\n {\n \"value\": \"600\",\n \"label\": \"600\"\n },\n {\n \"value\": \"700\",\n \"label\": \"800\"\n },\n {\n \"value\": \"800\",\n \"label\": \"800\"\n },\n {\n \"value\": \"900\",\n \"label\": \"900\"\n }\n ]\n },\n {\n \"key\": \"valueFont.color\",\n \"type\": \"color\"\n },\n {\n \"key\": \"valueFont.shadowColor\",\n \"type\": \"color\"\n }\n ]\n }, \n \"animation\",\n \"animationDuration\",\n {\n \"key\": \"animationRule\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"linear\",\n \"label\": \"Linear\"\n },\n {\n \"value\": \"quad\",\n \"label\": \"Quad\"\n },\n {\n \"value\": \"quint\",\n \"label\": \"Quint\"\n },\n {\n \"value\": \"cycle\",\n \"label\": \"Cycle\"\n },\n {\n \"value\": \"bounce\",\n \"label\": \"Bounce\"\n },\n {\n \"value\": \"elastic\",\n \"label\": \"Elastic\"\n },\n {\n \"value\": \"dequad\",\n \"label\": \"Dequad\"\n },\n {\n \"value\": \"dequint\",\n \"label\": \"Dequint\"\n },\n {\n \"value\": \"decycle\",\n \"label\": \"Decycle\"\n },\n {\n \"value\": \"debounce\",\n \"label\": \"Debounce\"\n },\n {\n \"value\": \"delastic\",\n \"label\": \"Delastic\"\n }\n ]\n }\n ]\n}","dataKeySettingsSchema":"{}\n","defaultConfig":"{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Speed\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 50 - 25;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 220) {\\n\\tvalue = 220;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"maxValue\":180,\"startAngle\":45,\"ticksAngle\":270,\"showBorder\":false,\"defaultColor\":\"#e65100\",\"needleCircleSize\":7,\"highlights\":[{\"from\":80,\"to\":120,\"color\":\"#fdd835\"},{\"color\":\"#e57373\",\"from\":120,\"to\":180}],\"showUnitTitle\":false,\"colorPlate\":\"#fff\",\"colorMajorTicks\":\"#444\",\"colorMinorTicks\":\"#666\",\"minorTicks\":2,\"valueInt\":3,\"minValue\":0,\"valueDec\":0,\"highlightsWidth\":15,\"valueBox\":true,\"animation\":true,\"animationDuration\":1500,\"animationRule\":\"linear\",\"colorNeedleShadowUp\":\"rgba(2, 255, 255, 0)\",\"colorNeedleShadowDown\":\"rgba(188, 143, 143, 0.78)\",\"units\":\"MPH\",\"majorTicksCount\":9,\"numbersFont\":{\"family\":\"RobotoDraft\",\"size\":22,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#616161\"},\"titleFont\":{\"family\":\"RobotoDraft\",\"size\":24,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#888\"},\"unitsFont\":{\"family\":\"RobotoDraft\",\"size\":28,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#616161\"},\"valueFont\":{\"size\":32,\"style\":\"normal\",\"weight\":\"normal\",\"shadowColor\":\"rgba(0, 0, 0, 0.49)\",\"color\":\"#444\",\"family\":\"Segment7Standard\"},\"colorValueBoxRect\":\"#888\",\"colorValueBoxRectEnd\":\"#666\",\"colorValueBoxBackground\":\"#babab2\",\"colorValueBoxShadow\":\"rgba(0,0,0,1)\"},\"title\":\"Speed gauge - Canvas Gauges\"}"}',
88 93 'Speed gauge - Canvas Gauges' );
... ...
... ... @@ -46,6 +46,7 @@ function WidgetService($rootScope, $http, $q, $filter, $ocLazyLoad, $window, typ
46 46 $window.TbAnalogueRadialGauge = TbAnalogueRadialGauge;
47 47 $window.TbDigitalGauge = TbDigitalGauge;
48 48 $window.TbMapWidget = TbMapWidget;
  49 + $window.cssjs = cssjs;
49 50
50 51 var cssParser = new cssjs();
51 52 cssParser.testMode = false;
... ...
... ... @@ -141,6 +141,14 @@ export default angular.module('thingsboard.types', [])
141 141 bundleAlias: "gpio_widgets",
142 142 alias: "basic_gpio_control"
143 143 }
  144 + },
  145 + static: {
  146 + value: "static",
  147 + name: "widget.static",
  148 + template: {
  149 + bundleAlias: "cards",
  150 + alias: "html_card"
  151 + }
144 152 }
145 153 },
146 154 systemBundleAlias: {
... ...
... ... @@ -139,6 +139,7 @@ function DashboardController($scope, $rootScope, $element, $timeout, $log, toast
139 139 vm.widgetBackgroundColor = widgetBackgroundColor;
140 140 vm.widgetPadding = widgetPadding;
141 141 vm.showWidgetTitle = showWidgetTitle;
  142 + vm.dropWidgetShadow = dropWidgetShadow;
142 143 vm.hasTimewindow = hasTimewindow;
143 144 vm.editWidget = editWidget;
144 145 vm.exportWidget = exportWidget;
... ... @@ -521,6 +522,14 @@ function DashboardController($scope, $rootScope, $element, $timeout, $log, toast
521 522 }
522 523 }
523 524
  525 + function dropWidgetShadow(widget) {
  526 + if (angular.isDefined(widget.config.dropShadow)) {
  527 + return widget.config.dropShadow;
  528 + } else {
  529 + return true;
  530 + }
  531 + }
  532 +
524 533 function hasTimewindow(widget) {
525 534 return widget.type === types.widgetType.timeseries.value;
526 535 }
... ...
... ... @@ -28,8 +28,10 @@
28 28 <li gridster-item="widget" ng-repeat="widget in vm.widgets">
29 29 <md-menu md-position-mode="target target" tb-mousepoint-menu>
30 30 <div tb-expand-fullscreen
31   - expand-button-id="expand-button" on-fullscreen-changed="vm.onWidgetFullscreenChanged(expanded, widget)" layout="column" class="tb-widget md-whiteframe-4dp"
32   - ng-class="{'tb-highlighted': vm.isHighlighted(widget), 'tb-not-highlighted': vm.isNotHighlighted(widget)}"
  31 + expand-button-id="expand-button" on-fullscreen-changed="vm.onWidgetFullscreenChanged(expanded, widget)" layout="column" class="tb-widget"
  32 + ng-class="{'tb-highlighted': vm.isHighlighted(widget),
  33 + 'tb-not-highlighted': vm.isNotHighlighted(widget),
  34 + 'md-whiteframe-4dp': vm.dropWidgetShadow(widget)}"
33 35 tb-mousedown="vm.widgetMouseDown($event, widget)"
34 36 tb-mousemove="vm.widgetMouseMove($event, widget)"
35 37 tb-mouseup="vm.widgetMouseUp($event, widget)"
... ...
  1 +/*
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +import React from 'react';
  17 +import ThingsboardAceEditor from './json-form-ace-editor.jsx';
  18 +import 'brace/mode/css';
  19 +import beautify from 'js-beautify';
  20 +
  21 +const css_beautify = beautify.css;
  22 +
  23 +class ThingsboardCss extends React.Component {
  24 +
  25 + constructor(props) {
  26 + super(props);
  27 + this.onTidyCss = this.onTidyCss.bind(this);
  28 + }
  29 +
  30 + onTidyCss(css) {
  31 + return css_beautify(css, {indent_size: 4});
  32 + }
  33 +
  34 + render() {
  35 + return (
  36 + <ThingsboardAceEditor {...this.props} mode='css' onTidy={this.onTidyCss} {...this.state}></ThingsboardAceEditor>
  37 + );
  38 + }
  39 +}
  40 +
  41 +export default ThingsboardCss;
... ...
  1 +/*
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +import React from 'react';
  17 +import ThingsboardAceEditor from './json-form-ace-editor.jsx';
  18 +import 'brace/mode/html';
  19 +import beautify from 'js-beautify';
  20 +
  21 +const html_beautify = beautify.html;
  22 +
  23 +class ThingsboardHtml extends React.Component {
  24 +
  25 + constructor(props) {
  26 + super(props);
  27 + this.onTidyHtml = this.onTidyHtml.bind(this);
  28 + }
  29 +
  30 + onTidyHtml(html) {
  31 + return html_beautify(html, {indent_size: 4});
  32 + }
  33 +
  34 + render() {
  35 + return (
  36 + <ThingsboardAceEditor {...this.props} mode='html' onTidy={this.onTidyHtml} {...this.state}></ThingsboardAceEditor>
  37 + );
  38 + }
  39 +}
  40 +
  41 +export default ThingsboardHtml;
... ...
... ... @@ -19,6 +19,8 @@ import { utils } from 'react-schema-form';
19 19 import ThingsboardArray from './json-form-array.jsx';
20 20 import ThingsboardJavaScript from './json-form-javascript.jsx';
21 21 import ThingsboardJson from './json-form-json.jsx';
  22 +import ThingsboardHtml from './json-form-html.jsx';
  23 +import ThingsboardCss from './json-form-css.jsx';
22 24 import ThingsboardColor from './json-form-color.jsx'
23 25 import ThingsboardRcSelect from './json-form-rc-select.jsx';
24 26 import ThingsboardNumber from './json-form-number.jsx';
... ... @@ -52,6 +54,8 @@ class ThingsboardSchemaForm extends React.Component {
52 54 'array': ThingsboardArray,
53 55 'javascript': ThingsboardJavaScript,
54 56 'json': ThingsboardJson,
  57 + 'html': ThingsboardHtml,
  58 + 'css': ThingsboardCss,
55 59 'color': ThingsboardColor,
56 60 'rc-select': ThingsboardRcSelect,
57 61 'fieldset': ThingsboardFieldSet
... ...
... ... @@ -74,11 +74,12 @@ function WidgetConfig($compile, $templateCache, $rootScope, types, utils) {
74 74 scope.selectedTab = 0;
75 75 scope.title = ngModelCtrl.$viewValue.title;
76 76 scope.showTitle = ngModelCtrl.$viewValue.showTitle;
  77 + scope.dropShadow = angular.isDefined(ngModelCtrl.$viewValue.dropShadow) ? ngModelCtrl.$viewValue.dropShadow : true;
77 78 scope.backgroundColor = ngModelCtrl.$viewValue.backgroundColor;
78 79 scope.color = ngModelCtrl.$viewValue.color;
79 80 scope.padding = ngModelCtrl.$viewValue.padding;
80 81 scope.timewindow = ngModelCtrl.$viewValue.timewindow;
81   - if (scope.widgetType !== types.widgetType.rpc.value) {
  82 + if (scope.widgetType !== types.widgetType.rpc.value && scope.widgetType !== types.widgetType.static.value) {
82 83 if (scope.datasources) {
83 84 scope.datasources.splice(0, scope.datasources.length);
84 85 } else {
... ... @@ -89,7 +90,7 @@ function WidgetConfig($compile, $templateCache, $rootScope, types, utils) {
89 90 scope.datasources.push({value: ngModelCtrl.$viewValue.datasources[i]});
90 91 }
91 92 }
92   - } else {
  93 + } else if (scope.widgetType === types.widgetType.rpc.value) {
93 94 if (ngModelCtrl.$viewValue.targetDeviceAliasIds && ngModelCtrl.$viewValue.targetDeviceAliasIds.length > 0) {
94 95 var aliasId = ngModelCtrl.$viewValue.targetDeviceAliasIds[0];
95 96 if (scope.deviceAliases[aliasId]) {
... ... @@ -140,18 +141,19 @@ function WidgetConfig($compile, $templateCache, $rootScope, types, utils) {
140 141 if (scope.widgetType === types.widgetType.rpc.value) {
141 142 valid = value && value.targetDeviceAliasIds && value.targetDeviceAliasIds.length > 0;
142 143 ngModelCtrl.$setValidity('targetDeviceAliasIds', valid);
143   - } else {
  144 + } else if (scope.widgetType !== types.widgetType.static.value) {
144 145 valid = value && value.datasources && value.datasources.length > 0;
145 146 ngModelCtrl.$setValidity('datasources', valid);
146 147 }
147 148 }
148 149 };
149 150
150   - scope.$watch('title + showTitle + backgroundColor + color + padding + intervalSec', function () {
  151 + scope.$watch('title + showTitle + dropShadow + backgroundColor + color + padding + intervalSec', function () {
151 152 if (ngModelCtrl.$viewValue) {
152 153 var value = ngModelCtrl.$viewValue;
153 154 value.title = scope.title;
154 155 value.showTitle = scope.showTitle;
  156 + value.dropShadow = scope.dropShadow;
155 157 value.backgroundColor = scope.backgroundColor;
156 158 value.color = scope.color;
157 159 value.padding = scope.padding;
... ... @@ -177,7 +179,8 @@ function WidgetConfig($compile, $templateCache, $rootScope, types, utils) {
177 179 }, true);
178 180
179 181 scope.$watch('datasources', function () {
180   - if (ngModelCtrl.$viewValue && scope.widgetType !== types.widgetType.rpc.value) {
  182 + if (ngModelCtrl.$viewValue && scope.widgetType !== types.widgetType.rpc.value
  183 + && scope.widgetType !== types.widgetType.static.value) {
181 184 var value = ngModelCtrl.$viewValue;
182 185 if (value.datasources) {
183 186 value.datasources.splice(0, value.datasources.length);
... ... @@ -235,7 +238,8 @@ function WidgetConfig($compile, $templateCache, $rootScope, types, utils) {
235 238 };
236 239
237 240 scope.updateDatasourcesAccordionState = function () {
238   - if (scope.widgetType !== types.widgetType.rpc.value) {
  241 + if (scope.widgetType !== types.widgetType.rpc.value &&
  242 + scope.widgetType !== types.widgetType.static.value) {
239 243 if (scope.datasourcesAccordion) {
240 244 scope.updateDatasourcesAccordionStatePending = false;
241 245 var expand = scope.datasources && scope.datasources.length < 4;
... ...
... ... @@ -31,6 +31,11 @@
31 31 ng-model="showTitle">{{ 'widget-config.display-title' | translate }}
32 32 </md-checkbox>
33 33 </div>
  34 + <div layout="row" layout-padding>
  35 + <md-checkbox flex aria-label="{{ 'widget-config.drop-shadow' | translate }}"
  36 + ng-model="dropShadow">{{ 'widget-config.drop-shadow' | translate }}
  37 + </md-checkbox>
  38 + </div>
34 39 <div flex
35 40 md-color-picker
36 41 ng-model="backgroundColor"
... ... @@ -64,7 +69,7 @@
64 69 <tb-timewindow as-button="true" flex ng-model="timewindow"></tb-timewindow>
65 70 </div>
66 71 <v-accordion id="datasources-accordion" control="datasourcesAccordion" class="vAccordion--default"
67   - ng-show="widgetType !== types.widgetType.rpc.value">
  72 + ng-show="widgetType !== types.widgetType.rpc.value && widgetType !== types.widgetType.static.value">
68 73 <v-pane id="datasources-pane" expanded="forceExpandDatasources">
69 74 <v-pane-header>
70 75 {{ 'widget-config.datasources' | translate }}
... ...
... ... @@ -66,6 +66,10 @@ export default function AddWidgetController($scope, widgetService, deviceService
66 66 link = 'widgetsConfigRpc';
67 67 break;
68 68 }
  69 + case types.widgetType.static.value: {
  70 + link = 'widgetsConfigStatic';
  71 + break;
  72 + }
69 73 }
70 74 }
71 75 return link;
... ...
... ... @@ -41,6 +41,7 @@ export default function DashboardController(types, widgetService, userService,
41 41 vm.latestWidgetTypes = [];
42 42 vm.timeseriesWidgetTypes = [];
43 43 vm.rpcWidgetTypes = [];
  44 + vm.staticWidgetTypes = [];
44 45 vm.widgetEditMode = $state.$current.data.widgetEditMode;
45 46 vm.widgets = [];
46 47
... ... @@ -82,6 +83,7 @@ export default function DashboardController(types, widgetService, userService,
82 83 vm.latestWidgetTypes = [];
83 84 vm.timeseriesWidgetTypes = [];
84 85 vm.rpcWidgetTypes = [];
  86 + vm.staticWidgetTypes = [];
85 87 if (vm.widgetsBundle) {
86 88 var bundleAlias = vm.widgetsBundle.alias;
87 89 var isSystem = vm.widgetsBundle.tenantId.id === types.id.nullUid;
... ... @@ -127,6 +129,8 @@ export default function DashboardController(types, widgetService, userService,
127 129 vm.latestWidgetTypes.push(widget);
128 130 } else if (widgetTypeInfo.type === types.widgetType.rpc.value) {
129 131 vm.rpcWidgetTypes.push(widget);
  132 + } else if (widgetTypeInfo.type === types.widgetType.static.value) {
  133 + vm.staticWidgetTypes.push(widget);
130 134 }
131 135 top += sizeY;
132 136 loadNextOrComplete(i);
... ... @@ -442,6 +446,10 @@ export default function DashboardController(types, widgetService, userService,
442 446 link = 'widgetsConfigRpc';
443 447 break;
444 448 }
  449 + case types.widgetType.static.value: {
  450 + link = 'widgetsConfigStatic';
  451 + break;
  452 + }
445 453 }
446 454 }
447 455 return link;
... ... @@ -490,6 +498,7 @@ export default function DashboardController(types, widgetService, userService,
490 498 vm.timeseriesWidgetTypes = [];
491 499 vm.latestWidgetTypes = [];
492 500 vm.rpcWidgetTypes = [];
  501 + vm.staticWidgetTypes = [];
493 502 }
494 503
495 504 function addWidgetFromType(event, widget) {
... ...
... ... @@ -136,7 +136,7 @@
136 136 </header-pane>
137 137 <div>
138 138 <md-tabs ng-if="vm.timeseriesWidgetTypes.length > 0 || vm.latestWidgetTypes.length > 0 ||
139   - vm.rpcWidgetTypes.length > 0"
  139 + vm.rpcWidgetTypes.length > 0 || vm.staticWidgetTypes.length > 0"
140 140 flex
141 141 class="tb-absolute-fill" md-border-bottom>
142 142 <md-tab ng-if="vm.timeseriesWidgetTypes.length > 0" style="height: 100%;" label="{{ 'widget.timeseries' | translate }}">
... ... @@ -169,9 +169,19 @@
169 169 on-widget-clicked="vm.addWidgetFromType(event, widget)">
170 170 </tb-dashboard>
171 171 </md-tab>
  172 + <md-tab ng-if="vm.staticWidgetTypes.length > 0" style="height: 100%;" label="{{ 'widget.static' | translate }}">
  173 + <tb-dashboard
  174 + widgets="vm.staticWidgetTypes"
  175 + is-edit="false"
  176 + is-mobile="true"
  177 + is-edit-action-enabled="false"
  178 + is-remove-action-enabled="false"
  179 + on-widget-clicked="vm.addWidgetFromType(event, widget)">
  180 + </tb-dashboard>
  181 + </md-tab>
172 182 </md-tabs>
173 183 <span translate ng-if="vm.timeseriesWidgetTypes.length === 0 && vm.latestWidgetTypes.length === 0 &&
174   - vm.rpcWidgetTypes.length === 0 && vm.widgetsBundle"
  184 + vm.rpcWidgetTypes.length === 0 && vm.staticWidgetTypes.length === 0 && vm.widgetsBundle"
175 185 layout-align="center center"
176 186 style="text-transform: uppercase; display: flex;"
177 187 class="md-headline tb-absolute-fill">widgets-bundle.empty</span>
... ...
... ... @@ -305,31 +305,33 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
305 305 for (var i = 0; i < widgetTypes.length; i++) {
306 306 var widgetType = widgetTypes[i];
307 307 var widgetInfo = widgetService.toWidgetInfo(widgetType);
308   - var sizeX = widgetInfo.sizeX*2;
309   - var sizeY = widgetInfo.sizeY*2;
310   - var col = Math.floor(Math.max(0, (20 - sizeX)/2));
311   - var widget = {
312   - isSystemType: isSystem,
313   - bundleAlias: bundleAlias,
314   - typeAlias: widgetInfo.alias,
315   - type: widgetInfo.type,
316   - title: widgetInfo.widgetName,
317   - sizeX: sizeX,
318   - sizeY: sizeY,
319   - row: 0,
320   - col: col,
321   - config: angular.fromJson(widgetInfo.defaultConfig)
322   - };
323   -
324   - widget.config.title = widgetInfo.widgetName;
325   - widget.config.datasources = [datasource];
326   - var length;
327   - if (scope.attributeScope === types.latestTelemetry && widgetInfo.type !== types.widgetType.rpc.value) {
328   - length = scope.widgetsListCache.push([widget]);
329   - scope.widgetsList.push(length === 1 ? [widget] : []);
330   - } else if (widgetInfo.type === types.widgetType.latest.value) {
331   - length = scope.widgetsListCache.push([widget]);
332   - scope.widgetsList.push(length === 1 ? [widget] : []);
  308 + if (widgetInfo.type !== types.widgetType.static.value) {
  309 + var sizeX = widgetInfo.sizeX * 2;
  310 + var sizeY = widgetInfo.sizeY * 2;
  311 + var col = Math.floor(Math.max(0, (20 - sizeX) / 2));
  312 + var widget = {
  313 + isSystemType: isSystem,
  314 + bundleAlias: bundleAlias,
  315 + typeAlias: widgetInfo.alias,
  316 + type: widgetInfo.type,
  317 + title: widgetInfo.widgetName,
  318 + sizeX: sizeX,
  319 + sizeY: sizeY,
  320 + row: 0,
  321 + col: col,
  322 + config: angular.fromJson(widgetInfo.defaultConfig)
  323 + };
  324 +
  325 + widget.config.title = widgetInfo.widgetName;
  326 + widget.config.datasources = [datasource];
  327 + var length;
  328 + if (scope.attributeScope === types.latestTelemetry && widgetInfo.type !== types.widgetType.rpc.value) {
  329 + length = scope.widgetsListCache.push([widget]);
  330 + scope.widgetsList.push(length === 1 ? [widget] : []);
  331 + } else if (widgetInfo.type === types.widgetType.latest.value) {
  332 + length = scope.widgetsListCache.push([widget]);
  333 + scope.widgetsList.push(length === 1 ? [widget] : []);
  334 + }
333 335 }
334 336 }
335 337 scope.widgetsLoaded = true;
... ...
... ... @@ -45,7 +45,7 @@ var pluginActionsClazzHelpLinkMap = {
45 45 'org.thingsboard.server.extensions.rest.action.RestApiCallPluginAction': 'pluginActionRestApiCall'
46 46 };
47 47
48   -var helpBaseUrl = "http://thingsboard.io";
  48 +var helpBaseUrl = "https://thingsboard.io";
49 49
50 50 export default angular.module('thingsboard.help', [])
51 51 .constant('helpLinks',
... ... @@ -86,6 +86,7 @@ export default angular.module('thingsboard.help', [])
86 86 widgetsConfigTimeseries: helpBaseUrl + "/docs/user-guide/ui/dashboards#timeseries",
87 87 widgetsConfigLatest: helpBaseUrl + "/docs/user-guide/ui/dashboards#latest",
88 88 widgetsConfigRpc: helpBaseUrl + "/docs/user-guide/ui/dashboards#rpc",
  89 + widgetsConfigStatic: helpBaseUrl + "/docs/user-guide/ui/dashboards#static",
89 90 },
90 91 getPluginLink: function(plugin) {
91 92 var link = 'plugins';
... ...
... ... @@ -53,6 +53,13 @@
53 53 </md-icon>
54 54 <span translate>{{vm.types.widgetType.rpc.name}}</span>
55 55 </md-button>
  56 + <md-button class="tb-card-button md-raised md-primary" layout="column"
  57 + ng-click="vm.typeSelected(vm.types.widgetType.static.value)">
  58 + <md-icon class="material-icons tb-md-96"
  59 + aria-label="{{ vm.types.widgetType.static.name | translate }}">font_download
  60 + </md-icon>
  61 + <span translate>{{vm.types.widgetType.static.name}}</span>
  62 + </md-button>
56 63 </div>
57 64 </fieldset>
58 65 </div>
... ...
... ... @@ -603,6 +603,7 @@
603 603 "timeseries": "Time series",
604 604 "latest-values": "Latest values",
605 605 "rpc": "Control widget",
  606 + "static": "Static widget",
606 607 "select-widget-type": "Select widget type",
607 608 "missing-widget-title-error": "Widget title must be specified!",
608 609 "widget-saved": "Widget saved",
... ... @@ -663,6 +664,7 @@
663 664 "title": "Title",
664 665 "general-settings": "General settings",
665 666 "display-title": "Display title",
  667 + "drop-shadow": "Drop shadow",
666 668 "background-color": "Background color",
667 669 "text-color": "Text color",
668 670 "padding": "Padding",
... ...