Browse Source

Improve qrcode widget. Add helps for qrcode and markdown widgets

pull/5422/head
Igor Kulikov 5 years ago
parent
commit
3e798d13aa
  1. 10
      application/src/main/data/json/system/widget_bundles/cards.json
  2. 15
      ui-ngx/src/app/modules/home/components/widget/lib/markdown-widget.component.ts
  3. 6
      ui-ngx/src/app/modules/home/components/widget/lib/qrcode-widget.component.html
  4. 22
      ui-ngx/src/app/modules/home/components/widget/lib/qrcode-widget.component.ts
  5. 58
      ui-ngx/src/assets/help/en_US/widget/lib/markdown/markdown_text_fn.md
  6. 55
      ui-ngx/src/assets/help/en_US/widget/lib/qrcode/qrcode_text_fn.md

10
application/src/main/data/json/system/widget_bundles/cards.json

@ -162,10 +162,10 @@
"resources": [],
"templateHtml": "<tb-qrcode-widget \n [ctx]=\"ctx\">\n</tb-qrcode-widget>",
"templateCss": "#container {\n overflow: auto;\n}\n\n.tbDatasource-container {\n margin: 5px;\n padding: 8px;\n}\n\n.tbDatasource-title {\n font-size: 1.200rem;\n font-weight: 500;\n padding-bottom: 10px;\n}\n\n.tbDatasource-table {\n width: 100%;\n box-shadow: 0 0 10px #ccc;\n border-collapse: collapse;\n white-space: nowrap;\n font-size: 1.000rem;\n color: #757575;\n}\n\n.tbDatasource-table td {\n position: relative;\n border-top: 1px solid rgba(0, 0, 0, 0.12);\n border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n padding: 0px 18px;\n box-sizing: border-box;\n}",
"controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.qrCodeWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n dataKeysOptional: true,\n singleEntity: true\n };\n}\n\nself.onDestroy = function() {\n}\n\n",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"QR Code\",\n \"properties\": {\n \"qrCodeTextPattern\": {\n \"title\": \"QR code text pattern (for ex. '${entityName} | ${keyName} - some text.')\",\n \"type\": \"string\",\n \"default\": \"${entityName}\"\n },\n \"useQrCodeTextFunction\": {\n \"title\": \"Use QR code text function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"qrCodeTextFunction\": {\n \"title\": \"QR code text function: f(data)\",\n \"type\": \"string\",\n \"default\": \"return data['entityName'];\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"qrCodeTextPattern\",\n \"useQrCodeTextFunction\",\n {\n \"key\": \"qrCodeTextFunction\",\n \"type\": \"javascript\"\n }\n ]\n}\n",
"controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.qrCodeWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n dataKeysOptional: true,\n datasourcesOptional: true\n };\n}\n\nself.onDestroy = function() {\n}\n\n",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"QR Code\",\n \"properties\": {\n \"qrCodeTextPattern\": {\n \"title\": \"QR code text pattern (for ex. '${entityName} | ${keyName} - some text.')\",\n \"type\": \"string\",\n \"default\": \"${entityName}\"\n },\n \"useQrCodeTextFunction\": {\n \"title\": \"Use QR code text function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"qrCodeTextFunction\": {\n \"title\": \"QR code text function: f(data)\",\n \"type\": \"string\",\n \"default\": \"return data[0]['entityName'];\"\n }\n },\n \"required\": []\n },\n \"form\": [\n {\n \"key\": \"qrCodeTextPattern\",\n \"condition\": \"model.useQrCodeTextFunction !== true\"\n },\n \"useQrCodeTextFunction\",\n {\n \"key\": \"qrCodeTextFunction\",\n \"type\": \"javascript\",\n \"helpId\": \"widget/lib/qrcode/qrcode_text_fn\",\n \"condition\": \"model.useQrCodeTextFunction === true\"\n }\n ]\n}\n",
"dataKeySettingsSchema": "{}\n",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7036904308224163,\"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\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"qrCodeTextPattern\":\"${entityName}\",\"useQrCodeTextFunction\":false,\"qrCodeTextFunction\":\"return data['entityName'];\"},\"title\":\"QR Code\"}"
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7036904308224163,\"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\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"qrCodeTextPattern\":\"${entityName}\",\"useQrCodeTextFunction\":false,\"qrCodeTextFunction\":\"return data[0] ? data[0]['entityName'] : '';\"},\"title\":\"QR Code\"}"
}
},
{
@ -181,9 +181,9 @@
"templateHtml": "<tb-markdown-widget \n [ctx]=\"ctx\">\n</tb-markdown-widget>",
"templateCss": "#container tb-markdown-widget {\n height: 100%;\n display: block;\n}\n\n#container tb-markdown-widget .tb-markdown-view {\n height: 100%;\n overflow: auto;\n}\n",
"controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.markdownWidget.onDataUpdated();\n}\n\nself.actionSources = function() {\n return {\n 'elementClick': {\n name: 'widget-action.element-click',\n multiple: true\n }\n };\n}\n\nself.typeParameters = function() {\n return {\n dataKeysOptional: true,\n datasourcesOptional: true\n };\n}\n\nself.onDestroy = function() {\n}\n\n",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Markdown/HTML card\",\n \"properties\": {\n \"markdownTextPattern\": {\n \"title\": \"Markdown/HTML pattern (markdown or HTML with variables, for ex. '${entityName} or ${keyName} - some text.')\",\n \"type\": \"string\",\n \"default\": \"# Markdown/HTML card \\n - **Current entity**: **${entityName}**. \\n - **Current value**: **${Random}**.\"\n },\n \"markdownCss\": {\n \"title\": \"Markdown/HTML CSS\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"useMarkdownTextFunction\": {\n \"title\": \"Use markdown/HTML value function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"markdownTextFunction\": {\n \"title\": \"Markdown/HTML value function: f(data)\",\n \"type\": \"string\",\n \"default\": \"return '# Some title\\\\n - Entity name: ' + data[0]['entityName'];\"\n }\n },\n \"required\": []\n },\n \"form\": [\n {\n \"key\": \"markdownTextPattern\",\n \"type\": \"markdown\"\n },\n {\n \"key\": \"markdownCss\",\n \"type\": \"css\"\n },\n \"useMarkdownTextFunction\",\n {\n \"key\": \"markdownTextFunction\",\n \"type\": \"javascript\"\n }\n ]\n}\n",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Markdown/HTML card\",\n \"properties\": {\n \"markdownTextPattern\": {\n \"title\": \"Markdown/HTML pattern (markdown or HTML with variables, for ex. '${entityName} or ${keyName} - some text.')\",\n \"type\": \"string\",\n \"default\": \"# Markdown/HTML card \\n - **Current entity**: **${entityName}**. \\n - **Current value**: **${Random}**.\"\n },\n \"markdownCss\": {\n \"title\": \"Markdown/HTML CSS\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"useMarkdownTextFunction\": {\n \"title\": \"Use markdown/HTML value function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"markdownTextFunction\": {\n \"title\": \"Markdown/HTML value function: f(data)\",\n \"type\": \"string\",\n \"default\": \"return '# Some title\\\\n - Entity name: ' + data[0]['entityName'];\"\n }\n },\n \"required\": []\n },\n \"form\": [\n {\n \"key\": \"markdownTextPattern\",\n \"type\": \"markdown\",\n \"condition\": \"model.useMarkdownTextFunction !== true\"\n },\n {\n \"key\": \"markdownCss\",\n \"type\": \"css\"\n },\n \"useMarkdownTextFunction\",\n {\n \"key\": \"markdownTextFunction\",\n \"type\": \"javascript\",\n \"helpId\": \"widget/lib/markdown/markdown_text_fn\",\n \"condition\": \"model.useMarkdownTextFunction === true\"\n }\n ]\n}\n",
"dataKeySettingsSchema": "{}\n",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"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\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"markdownTextPattern\":\"### Markdown/HTML card\\n - **Current entity**: ${entityName}.\\n - **Current value**: ${Random}.\",\"markdownTextFunction\":\"return '# Some title\\\\n - Entity name: ' + data[0]['entityName'];\"},\"title\":\"Markdown/HTML Card\",\"showTitleIcon\":false,\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"showLegend\":false}"
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"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\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"markdownTextPattern\":\"### Markdown/HTML card\\n - **Current entity**: ${entityName}.\\n - **Current value**: ${Random}.\",\"markdownTextFunction\":\"return '# Some title\\\\n - Entity name: ' + data[0]['entityName'];\",\"useMarkdownTextFunction\":false},\"title\":\"Markdown/HTML Card\",\"showTitleIcon\":false,\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"showLegend\":false}"
}
}
]

15
ui-ngx/src/app/modules/home/components/widget/lib/markdown-widget.component.ts

@ -99,15 +99,12 @@ export class MarkdownWidgetComponent extends PageComponent implements OnInit {
} else {
initialData = [];
}
let markdownText: string;
if (initialData) {
const data = parseData(initialData);
markdownText = this.settings.useMarkdownTextFunction ?
safeExecute(this.markdownTextFunction, [data]) : this.settings.markdownTextPattern;
const allData = flatData(data);
const replaceInfo = processPattern(markdownText, allData);
markdownText = fillPattern(markdownText, replaceInfo, allData);
}
const data = parseData(initialData);
let markdownText = this.settings.useMarkdownTextFunction ?
safeExecute(this.markdownTextFunction, [data]) : this.settings.markdownTextPattern;
const allData = flatData(data);
const replaceInfo = processPattern(markdownText, allData);
markdownText = fillPattern(markdownText, replaceInfo, allData);
if (this.markdownText !== markdownText) {
this.markdownText = this.utils.customTranslation(markdownText, markdownText);
this.cd.detectChanges();

6
ui-ngx/src/app/modules/home/components/widget/lib/qrcode-widget.component.html

@ -17,8 +17,6 @@
-->
<div fxLayout="column" fxLayoutAlign="center center" style="width: 100%; height: 100%;">
<canvas fxFlex #canvas [ngStyle]="{display: qrCodeText && !invalidQrCodeText ? 'block' : 'none'}"></canvas>
<div *ngIf="datasourceDetected">
<div *ngIf="!qrCodeText && !invalidQrCodeText" style="text-align: center" translate>entity.no-data</div>
<div *ngIf="invalidQrCodeText" style="text-align: center" translate>widgets.invalid-qr-code-text</div>
</div>
<div *ngIf="!qrCodeText && !invalidQrCodeText" style="text-align: center" translate>entity.no-data</div>
<div *ngIf="invalidQrCodeText" style="text-align: center" translate>widgets.invalid-qr-code-text</div>
</div>

22
ui-ngx/src/app/modules/home/components/widget/lib/qrcode-widget.component.ts

@ -20,7 +20,7 @@ import { WidgetContext } from '@home/models/widget-component.models';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import {
fillPattern,
fillPattern, flatData,
parseData,
parseFunction,
processPattern,
@ -37,7 +37,7 @@ interface QrCodeWidgetSettings {
qrCodeTextFunction: string;
}
type QrCodeTextFunction = (data: FormattedData) => string;
type QrCodeTextFunction = (data: FormattedData[]) => string;
@Component({
selector: 'tb-qrcode-widget',
@ -54,7 +54,6 @@ export class QrCodeWidgetComponent extends PageComponent implements OnInit, Afte
qrCodeText: string;
invalidQrCodeText = false;
datasourceDetected = false;
private viewInited: boolean;
private scheduleUpdateCanvas: boolean;
@ -97,17 +96,14 @@ export class QrCodeWidgetComponent extends PageComponent implements OnInit, Afte
}
];
} else {
this.datasourceDetected = false;
}
if (initialData) {
const data = parseData(initialData);
const dataSourceData = data[0];
const pattern = this.settings.useQrCodeTextFunction ?
safeExecute(this.qrCodeTextFunction, [dataSourceData]) : this.settings.qrCodeTextPattern;
const replaceInfo = processPattern(pattern, dataSourceData);
qrCodeText = fillPattern(pattern, replaceInfo, dataSourceData);
this.datasourceDetected = true;
initialData = [];
}
const data = parseData(initialData);
const pattern = this.settings.useQrCodeTextFunction ?
safeExecute(this.qrCodeTextFunction, [data]) : this.settings.qrCodeTextPattern;
const allData = flatData(data);
const replaceInfo = processPattern(pattern, allData);
qrCodeText = fillPattern(pattern, replaceInfo, allData);
this.updateQrCodeText(qrCodeText);
}

58
ui-ngx/src/assets/help/en_US/widget/lib/markdown/markdown_text_fn.md

@ -0,0 +1,58 @@
#### Markdown text function
<div class="divider"></div>
<br/>
*function (data): string*
A JavaScript function used to calculate markdown or HTML content.
**Parameters:**
<ul>
<li><b>data:</b> <code><a href="https://github.com/thingsboard/thingsboard/blob/5bb6403407aa4898084832d6698aa9ea6d484889/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts#L108" target="_blank">FormattedData[]</a></code> - An array of <a href="https://github.com/thingsboard/thingsboard/blob/5bb6403407aa4898084832d6698aa9ea6d484889/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts#L108" target="_blank">FormattedData</a> objects resolved from configured datasources.<br/>
Each object represents basic entity properties (ex. <code>entityId</code>, <code>entityName</code>)<br/>and provides access to other entity attributes/timeseries declared in widget datasource configuration.
</li>
</ul>
**Returns:**
Should return string value presenting markdown or HTML content.
<div class="divider"></div>
##### Examples
* Display markdown with first entity name information:
```javascript
return '# Some title\n - Entity name: ' + data[0]['entityName'];
{:copy-code}
```
<ul>
<li>
Display greetings for currently logged-in user.<br>
Let's assume widget has first datasource configured using <code>Current User</code> <a target="_blank" href="${baseUrl}/docs/user-guide/ui/aliases/#single-entity">Single entity</a> alias<br>
and has data keys for <code>firstName</code>, <code>lastName</code> and <code>name</code> entity fields:
</li>
</ul>
```javascript
var userEntity = data[0];
var userName;
if (userEntity.firstName || userEntity['First name']) {
userName = userEntity.firstName || userEntity['First name'];
} else if (userEntity.lastName || userEntity['Last name']) {
userName = userEntity.lastName || userEntity['Last name'];
} else if (userEntity.name || userEntity['Name']) {
userName = userEntity.name || userEntity['Name'];
}
var welcomeText = 'Hi, ' + userName + '!\n\n';
return welcomeText;
{:copy-code}
```
<br>
<br>

55
ui-ngx/src/assets/help/en_US/widget/lib/qrcode/qrcode_text_fn.md

@ -0,0 +1,55 @@
#### QR code text function
<div class="divider"></div>
<br/>
*function (data): string*
A JavaScript function used to calculate text to be displayed as QR code.
**Parameters:**
<ul>
<li><b>data:</b> <code><a href="https://github.com/thingsboard/thingsboard/blob/5bb6403407aa4898084832d6698aa9ea6d484889/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts#L108" target="_blank">FormattedData[]</a></code> - An array of <a href="https://github.com/thingsboard/thingsboard/blob/5bb6403407aa4898084832d6698aa9ea6d484889/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts#L108" target="_blank">FormattedData</a> objects resolved from configured datasources.<br/>
Each object represents basic entity properties (ex. <code>entityId</code>, <code>entityName</code>)<br/>and provides access to other entity attributes/timeseries declared in widget datasource configuration.
</li>
</ul>
**Returns:**
Should return string value presenting text to be displayed as QR code.
<div class="divider"></div>
##### Examples
* Prepare QR code text from name of the first entity if present:
```javascript
return data[0] ? data[0]['entityName'] : '';
{:copy-code}
```
<ul>
<li>
Prepare QR code text to use as device claiming info (in this case <code>{deviceName: string, secretKey: string}</code>).<br>
Let's assume device has <code>claimingData</code> attribute with string JSON value containing <code>secretKey</code> field<br>
(see <a target="_blank" href="${baseUrl}/docs/user-guide/claiming-devices/">Claiming devices</a>):
</li>
</ul>
```javascript
var entityData = data[0];
if (entityData) {
return JSON.stringify({
deviceName: entityData.entityName,
secretKey: JSON.parse(entityData.claimingData).secretKey
});
} else {
return '';
}
{:copy-code}
```
<br>
<br>
Loading…
Cancel
Save