Showing
15 changed files
with
502 additions
and
11 deletions
... | ... | @@ -167,6 +167,24 @@ |
167 | 167 | "dataKeySettingsSchema": "{}\n", |
168 | 168 | "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\"}" |
169 | 169 | } |
170 | + }, | |
171 | + { | |
172 | + "alias": "markdown_card", | |
173 | + "name": "Markdown Card", | |
174 | + "image": "data:image/png;base64,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", | |
175 | + "description": "Renders markdown/HTML using configurable pattern or function with applied attributes or timeseries values.", | |
176 | + "descriptor": { | |
177 | + "type": "latest", | |
178 | + "sizeX": 5, | |
179 | + "sizeY": 3.5, | |
180 | + "resources": [], | |
181 | + "templateHtml": "<tb-markdown-widget \n [ctx]=\"ctx\">\n</tb-markdown-widget>", | |
182 | + "templateCss": "#container {\n overflow: auto;\n}", | |
183 | + "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.onDestroy = function() {\n}\n\n", | |
184 | + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Markdown card\",\n \"properties\": {\n \"markdownTextPattern\": {\n \"title\": \"Markdown pattern (markdown with variables, for ex. '${entityName} or ${keyName} - some text.')\",\n \"type\": \"string\",\n \"default\": \"# Markdown card \\n - **Current entity**: **${entityName}**. \\n - **Current value**: **${Random}**.\"\n },\n \"markdownCss\": {\n \"title\": \"Markdown CSS\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"useMarkdownTextFunction\": {\n \"title\": \"Use markdown text function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"markdownTextFunction\": {\n \"title\": \"Markdown text 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", | |
185 | + "dataKeySettingsSchema": "{}\n", | |
186 | + "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\":\"16px\",\"settings\":{\"markdownTextPattern\":\"### Markdown card\\n - **Current entity**: ${entityName}.\\n - **Current value**: ${Random}.\",\"markdownTextFunction\":\"return '# Some title\\\\n - Entity name: ' + data[0]['entityName'];\"},\"title\":\"Markdown 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}" | |
187 | + } | |
170 | 188 | } |
171 | 189 | ] |
172 | 190 | } | ... | ... |
... | ... | @@ -78,7 +78,8 @@ |
78 | 78 | "node_modules/leaflet/dist/leaflet.css", |
79 | 79 | "src/app/modules/home/components/widget/lib/maps/markers.scss", |
80 | 80 | "node_modules/leaflet.markercluster/dist/MarkerCluster.css", |
81 | - "node_modules/leaflet.markercluster/dist/MarkerCluster.Default.css" | |
81 | + "node_modules/leaflet.markercluster/dist/MarkerCluster.Default.css", | |
82 | + "node_modules/prismjs/themes/prism.css" | |
82 | 83 | ], |
83 | 84 | "stylePreprocessorOptions": { |
84 | 85 | "includePaths": [ |
... | ... | @@ -88,7 +89,11 @@ |
88 | 89 | "scripts": [ |
89 | 90 | "node_modules/tinycolor2/dist/tinycolor-min.js", |
90 | 91 | "node_modules/split.js/dist/split.min.js", |
91 | - "node_modules/systemjs/dist/system.js" | |
92 | + "node_modules/systemjs/dist/system.js", | |
93 | + "node_modules/marked/lib/marked.js", | |
94 | + "node_modules/prismjs/prism.js", | |
95 | + "node_modules/prismjs/components/prism-bash.min.js", | |
96 | + "node_modules/prismjs/components/prism-json.min.js" | |
92 | 97 | ], |
93 | 98 | "customWebpackConfig": { |
94 | 99 | "path": "./extra-webpack.config.js" | ... | ... |
... | ... | @@ -72,6 +72,7 @@ |
72 | 72 | "ngx-drag-drop": "^2.0.0", |
73 | 73 | "ngx-flowchart": "git://github.com/thingsboard/ngx-flowchart.git#master", |
74 | 74 | "ngx-hm-carousel": "^2.0.0-rc.1", |
75 | + "ngx-markdown": "^10.1.1", | |
75 | 76 | "ngx-sharebuttons": "^8.0.5", |
76 | 77 | "ngx-translate-messageformat-compiler": "^4.9.0", |
77 | 78 | "objectpath": "^2.0.0", | ... | ... |
... | ... | @@ -16,7 +16,7 @@ |
16 | 16 | |
17 | 17 | import { FormattedData, MapProviders, ReplaceInfo } from '@home/components/widget/lib/maps/map-models'; |
18 | 18 | import { |
19 | - createLabelFromDatasource, | |
19 | + createLabelFromDatasource, deepClone, | |
20 | 20 | hashCode, |
21 | 21 | isDefined, |
22 | 22 | isDefinedAndNotNull, |
... | ... | @@ -343,6 +343,22 @@ export function parseData(input: DatasourceData[]): FormattedData[] { |
343 | 343 | }); |
344 | 344 | } |
345 | 345 | |
346 | +export function flatData(input: FormattedData[]): FormattedData { | |
347 | + let result: FormattedData = {} as FormattedData; | |
348 | + if (input.length) { | |
349 | + for (const toMerge of input) { | |
350 | + result = {...result, ...toMerge}; | |
351 | + } | |
352 | + result.entityName = input[0].entityName; | |
353 | + result.entityId = input[0].entityId; | |
354 | + result.entityType = input[0].entityType; | |
355 | + result.$datasource = input[0].$datasource; | |
356 | + result.dsIndex = input[0].dsIndex; | |
357 | + result.deviceType = input[0].deviceType; | |
358 | + } | |
359 | + return result; | |
360 | +} | |
361 | + | |
346 | 362 | export function parseArray(input: DatasourceData[]): FormattedData[][] { |
347 | 363 | return _(input).groupBy(el => el?.datasource?.entityName) |
348 | 364 | .values().value().map((entityArray) => | ... | ... |
1 | +<!-- | |
2 | + | |
3 | + Copyright © 2016-2021 The Thingsboard Authors | |
4 | + | |
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | + you may not use this file except in compliance with the License. | |
7 | + You may obtain a copy of the License at | |
8 | + | |
9 | + http://www.apache.org/licenses/LICENSE-2.0 | |
10 | + | |
11 | + Unless required by applicable law or agreed to in writing, software | |
12 | + distributed under the License is distributed on an "AS IS" BASIS, | |
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | + See the License for the specific language governing permissions and | |
15 | + limitations under the License. | |
16 | + | |
17 | +--> | |
18 | +<markdown [data]="markdownText" class="tb-markdown-view" (click)="markdownClick($event)"></markdown> | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2021 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 | + | |
17 | +.tb-markdown-view { | |
18 | + display: block; | |
19 | + ::ng-deep { | |
20 | + h1 { | |
21 | + font-size: 32px; | |
22 | + padding-right: 60px; | |
23 | + } | |
24 | + | |
25 | + h1, h2, h3, h4, h5, h6 { | |
26 | + line-height: normal; | |
27 | + font-weight: 500; | |
28 | + margin-bottom: 30px; | |
29 | + padding-bottom: 10px; | |
30 | + border-bottom: 1px solid #ccc; | |
31 | + } | |
32 | + | |
33 | + p { | |
34 | + font-size: 16px; | |
35 | + font-weight: 400; | |
36 | + line-height: 1.25em; | |
37 | + margin: 0; | |
38 | + } | |
39 | + | |
40 | + p+p { | |
41 | + margin-top: 10px; | |
42 | + } | |
43 | + | |
44 | + table { | |
45 | + width: 100%; | |
46 | + border: 1px solid #ccc; | |
47 | + border-spacing: 0; | |
48 | + margin-top: 30px; | |
49 | + margin-bottom: 30px; | |
50 | + } | |
51 | + | |
52 | + thead { | |
53 | + background-color: #555; | |
54 | + color: #fff; | |
55 | + } | |
56 | + | |
57 | + th, td { | |
58 | + font-size: .85em; | |
59 | + padding: 8px; | |
60 | + margin: 0; | |
61 | + text-align: left; | |
62 | + } | |
63 | + | |
64 | + td[align=center], th[align=center] { | |
65 | + text-align: center; | |
66 | + } | |
67 | + | |
68 | + td[align=right], th[align=right] { | |
69 | + text-align: right; | |
70 | + } | |
71 | + | |
72 | + tr:nth-child(even) { | |
73 | + background-color: #f7f7f7; | |
74 | + } | |
75 | + | |
76 | + code:not([class*=language-]) { | |
77 | + background: #f5f5f5; | |
78 | + border-radius: 2px; | |
79 | + color: #dd4a68; | |
80 | + padding: 2px 4px; | |
81 | + } | |
82 | + | |
83 | + div.code-wrapper { | |
84 | + position: relative; | |
85 | + button.clipboard-btn { | |
86 | + cursor: pointer; | |
87 | + margin: 0; | |
88 | + border: 0; | |
89 | + outline: none; | |
90 | + position: absolute; | |
91 | + top: 5px; | |
92 | + right: 5px; | |
93 | + background: #fff; | |
94 | + box-shadow: 0 1px 8px 0 rgba(0,0,0,0.2), 0 3px 4px 0 rgba(0,0,0,0.14), 0 3px 3px -2px rgba(0,0,0,0.12); | |
95 | + border-radius: 5px; | |
96 | + opacity: 0; | |
97 | + transition: opacity .3s; | |
98 | + padding: 3px 6px; | |
99 | + line-height: 16px; | |
100 | + img { | |
101 | + width: 18px; | |
102 | + } | |
103 | + &:hover { | |
104 | + background: #f9f9f9; | |
105 | + } | |
106 | + &:active { | |
107 | + background-color: #ececec; | |
108 | + box-shadow: inset 1px -1px 4px 0px rgba(0,0,0,0.4); | |
109 | + } | |
110 | + } | |
111 | + &:hover { | |
112 | + button.clipboard-btn { | |
113 | + opacity: .85; | |
114 | + } | |
115 | + } | |
116 | + } | |
117 | + | |
118 | + th, td { | |
119 | + div.code-wrapper { | |
120 | + display: inline-block; | |
121 | + button.clipboard-btn { | |
122 | + top: -5px; | |
123 | + right: -30px; | |
124 | + padding: 0 3px; | |
125 | + } | |
126 | + } | |
127 | + } | |
128 | + | |
129 | + } | |
130 | +} | ... | ... |
1 | +/// | |
2 | +/// Copyright © 2016-2021 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 | + | |
17 | +import { ChangeDetectorRef, Component, ElementRef, Input, OnInit } from '@angular/core'; | |
18 | +import { PageComponent } from '@shared/components/page.component'; | |
19 | +import { WidgetContext } from '@home/models/widget-component.models'; | |
20 | +import { Store } from '@ngrx/store'; | |
21 | +import { AppState } from '@core/core.state'; | |
22 | +import { DatasourceData } from '@shared/models/widget.models'; | |
23 | +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; | |
24 | +import { | |
25 | + fillPattern, flatData, | |
26 | + parseData, | |
27 | + parseFunction, | |
28 | + processPattern, | |
29 | + safeExecute | |
30 | +} from '@home/components/widget/lib/maps/common-maps-utils'; | |
31 | +import { FormattedData } from '@home/components/widget/lib/maps/map-models'; | |
32 | +import { hashCode, isNotEmptyStr } from '@core/utils'; | |
33 | +import cssjs from '@core/css/css'; | |
34 | + | |
35 | +interface MarkdownWidgetSettings { | |
36 | + markdownTextPattern: string; | |
37 | + useMarkdownTextFunction: boolean; | |
38 | + markdownTextFunction: string; | |
39 | + markdownCss: string; | |
40 | +} | |
41 | + | |
42 | +type MarkdownTextFunction = (data: FormattedData[]) => string; | |
43 | + | |
44 | +@Component({ | |
45 | + selector: 'tb-markdown-widget ', | |
46 | + templateUrl: './markdown-widget.component.html', | |
47 | + styleUrls: ['./markdown-widget.component.scss'] | |
48 | +}) | |
49 | +export class MarkdownWidgetComponent extends PageComponent implements OnInit { | |
50 | + | |
51 | + settings: MarkdownWidgetSettings; | |
52 | + markdownTextFunction: MarkdownTextFunction; | |
53 | + | |
54 | + @Input() | |
55 | + ctx: WidgetContext; | |
56 | + | |
57 | + markdownText: string; | |
58 | + | |
59 | + constructor(protected store: Store<AppState>, | |
60 | + private elementRef: ElementRef, | |
61 | + private cd: ChangeDetectorRef) { | |
62 | + super(store); | |
63 | + } | |
64 | + | |
65 | + ngOnInit(): void { | |
66 | + this.ctx.$scope.markdownWidget = this; | |
67 | + this.settings = this.ctx.settings; | |
68 | + this.markdownTextFunction = this.settings.useMarkdownTextFunction ? parseFunction(this.settings.markdownTextFunction, ['data']) : null; | |
69 | + | |
70 | + const cssString = this.settings.markdownCss; | |
71 | + if (isNotEmptyStr(cssString)) { | |
72 | + const cssParser = new cssjs(); | |
73 | + cssParser.testMode = false; | |
74 | + const namespace = 'entities-hierarchy-' + hashCode(cssString); | |
75 | + cssParser.cssPreviewNamespace = namespace; | |
76 | + cssParser.createStyleElement(namespace, cssString); | |
77 | + $(this.elementRef.nativeElement).addClass(namespace); | |
78 | + } | |
79 | + } | |
80 | + | |
81 | + public onDataUpdated() { | |
82 | + let initialData: DatasourceData[]; | |
83 | + if (this.ctx.data?.length) { | |
84 | + initialData = this.ctx.data; | |
85 | + } else if (this.ctx.datasources?.length) { | |
86 | + initialData = [ | |
87 | + { | |
88 | + datasource: this.ctx.datasources[0], | |
89 | + dataKey: { | |
90 | + type: DataKeyType.attribute, | |
91 | + name: 'empty' | |
92 | + }, | |
93 | + data: [] | |
94 | + } | |
95 | + ]; | |
96 | + } | |
97 | + let markdownText: string; | |
98 | + if (initialData) { | |
99 | + const data = parseData(initialData); | |
100 | + markdownText = this.settings.useMarkdownTextFunction ? | |
101 | + safeExecute(this.markdownTextFunction, [data]) : this.settings.markdownTextPattern; | |
102 | + const allData = flatData(data); | |
103 | + const replaceInfo = processPattern(markdownText, allData); | |
104 | + markdownText = fillPattern(markdownText, replaceInfo, allData); | |
105 | + } | |
106 | + if (this.markdownText !== markdownText) { | |
107 | + this.markdownText = markdownText; | |
108 | + this.cd.detectChanges(); | |
109 | + } | |
110 | + } | |
111 | + | |
112 | + markdownClick($event: MouseEvent) { | |
113 | + this.ctx.actionsApi.elementClick($event); | |
114 | + } | |
115 | + | |
116 | +} | ... | ... |
... | ... | @@ -101,7 +101,7 @@ export class QrCodeWidgetComponent extends PageComponent implements OnInit, Afte |
101 | 101 | const dataSourceData = data[0]; |
102 | 102 | const pattern = this.settings.useQrCodeTextFunction ? |
103 | 103 | safeExecute(this.qrCodeTextFunction, [dataSourceData]) : this.settings.qrCodeTextPattern; |
104 | - const replaceInfo = processPattern(pattern, data); | |
104 | + const replaceInfo = processPattern(pattern, dataSourceData); | |
105 | 105 | qrCodeText = fillPattern(pattern, replaceInfo, dataSourceData); |
106 | 106 | } |
107 | 107 | this.updateQrCodeText(qrCodeText); | ... | ... |
... | ... | @@ -40,6 +40,7 @@ import { NavigationCardWidgetComponent } from '@home/components/widget/lib/navig |
40 | 40 | import { EdgesOverviewWidgetComponent } from '@home/components/widget/lib/edges-overview-widget.component'; |
41 | 41 | import { JsonInputWidgetComponent } from '@home/components/widget/lib/json-input-widget.component'; |
42 | 42 | import { QrCodeWidgetComponent } from '@home/components/widget/lib/qrcode-widget.component'; |
43 | +import { MarkdownWidgetComponent } from '@home/components/widget/lib/markdown-widget.component'; | |
43 | 44 | |
44 | 45 | @NgModule({ |
45 | 46 | declarations: |
... | ... | @@ -60,7 +61,8 @@ import { QrCodeWidgetComponent } from '@home/components/widget/lib/qrcode-widget |
60 | 61 | GatewayFormComponent, |
61 | 62 | NavigationCardsWidgetComponent, |
62 | 63 | NavigationCardWidgetComponent, |
63 | - QrCodeWidgetComponent | |
64 | + QrCodeWidgetComponent, | |
65 | + MarkdownWidgetComponent | |
64 | 66 | ], |
65 | 67 | imports: [ |
66 | 68 | CommonModule, |
... | ... | @@ -83,7 +85,8 @@ import { QrCodeWidgetComponent } from '@home/components/widget/lib/qrcode-widget |
83 | 85 | GatewayFormComponent, |
84 | 86 | NavigationCardsWidgetComponent, |
85 | 87 | NavigationCardWidgetComponent, |
86 | - QrCodeWidgetComponent | |
88 | + QrCodeWidgetComponent, | |
89 | + MarkdownWidgetComponent | |
87 | 90 | ], |
88 | 91 | providers: [ |
89 | 92 | CustomDialogService, | ... | ... |
... | ... | @@ -159,8 +159,8 @@ class ThingsboardAceEditor extends React.Component<ThingsboardAceEditorProps, Th |
159 | 159 | <div className='json-form-ace-editor'> |
160 | 160 | <div className='title-panel'> |
161 | 161 | <label>{this.props.mode}</label> |
162 | - <Button style={ styles.tidyButtonStyle } | |
163 | - className='tidy-button' onClick={this.onTidy}>Tidy</Button> | |
162 | + { this.props.onTidy ? <Button style={ styles.tidyButtonStyle } | |
163 | + className='tidy-button' onClick={this.onTidy}>Tidy</Button> : null } | |
164 | 164 | <Button style={ styles.tidyButtonStyle } |
165 | 165 | className='tidy-button' onClick={this.onToggleFull}> |
166 | 166 | {this.state.isFull ? | ... | ... |
1 | +/* | |
2 | + * Copyright © 2016-2021 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 * as React from 'react'; | |
17 | +import ThingsboardAceEditor from './json-form-ace-editor'; | |
18 | +import { JsonFormFieldProps, JsonFormFieldState } from '@shared/components/json-form/react/json-form.models'; | |
19 | + | |
20 | +class ThingsboardMarkdown extends React.Component<JsonFormFieldProps, JsonFormFieldState> { | |
21 | + | |
22 | + constructor(props) { | |
23 | + super(props); | |
24 | + } | |
25 | + | |
26 | + render() { | |
27 | + return ( | |
28 | + <ThingsboardAceEditor {...this.props} mode='markdown' {...this.state}></ThingsboardAceEditor> | |
29 | + ); | |
30 | + } | |
31 | +} | |
32 | + | |
33 | +export default ThingsboardMarkdown; | ... | ... |
... | ... | @@ -38,6 +38,7 @@ import { JsonFormData, JsonFormProps, onChangeFn, OnColorClickFn, OnIconClickFn |
38 | 38 | import _ from 'lodash'; |
39 | 39 | import * as tinycolor_ from 'tinycolor2'; |
40 | 40 | import { GroupInfo } from '@shared/models/widget.models'; |
41 | +import ThingsboardMarkdown from '@shared/components/json-form/react/json-form-markdown'; | |
41 | 42 | |
42 | 43 | const tinycolor = tinycolor_; |
43 | 44 | |
... | ... | @@ -65,6 +66,7 @@ class ThingsboardSchemaForm extends React.Component<JsonFormProps, any> { |
65 | 66 | json: ThingsboardJson, |
66 | 67 | html: ThingsboardHtml, |
67 | 68 | css: ThingsboardCss, |
69 | + markdown: ThingsboardMarkdown, | |
68 | 70 | color: ThingsboardColor, |
69 | 71 | 'rc-select': ThingsboardRcSelect, |
70 | 72 | fieldset: ThingsboardFieldSet, |
... | ... | @@ -91,7 +93,7 @@ class ThingsboardSchemaForm extends React.Component<JsonFormProps, any> { |
91 | 93 | } |
92 | 94 | |
93 | 95 | onIconClick(key: (string | number)[], val: string, |
94 | - iconSelectedFn: (icon: string) => void) { | |
96 | + iconSelectedFn: (icon: string) => void) { | |
95 | 97 | this.props.onIconClick(key, val, iconSelectedFn); |
96 | 98 | } |
97 | 99 | ... | ... |
1 | +/// | |
2 | +/// Copyright © 2016-2021 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 | + | |
17 | + | |
18 | +import { MarkedOptions, MarkedRenderer } from 'ngx-markdown'; | |
19 | + | |
20 | +export function markedOptionsFactory(): MarkedOptions { | |
21 | + const renderer = new MarkedRenderer(); | |
22 | + const renderer2 = new MarkedRenderer(); | |
23 | + | |
24 | + const copyCodeBlock = '{:copy-code}'; | |
25 | + | |
26 | + let id = 1; | |
27 | + | |
28 | + renderer.code = (code: string, language: string | undefined, isEscaped: boolean) => { | |
29 | + if (code.endsWith(copyCodeBlock)) { | |
30 | + code = code.substring(0, code.length - copyCodeBlock.length); | |
31 | + const content = renderer2.code(code, language, isEscaped); | |
32 | + id++; | |
33 | + return wrapCopyCode(id, content, code); | |
34 | + } else { | |
35 | + return renderer2.code(code, language, isEscaped); | |
36 | + } | |
37 | + }; | |
38 | + | |
39 | + renderer.tablecell = (content: string, flags: { | |
40 | + header: boolean; | |
41 | + align: 'center' | 'left' | 'right' | null; | |
42 | + }) => { | |
43 | + if (content.endsWith(copyCodeBlock)) { | |
44 | + content = content.substring(0, content.length - copyCodeBlock.length); | |
45 | + id++; | |
46 | + content = wrapCopyCode(id, content, content); | |
47 | + } | |
48 | + return renderer2.tablecell(content, flags); | |
49 | + }; | |
50 | + | |
51 | + return { | |
52 | + renderer, | |
53 | + headerIds: true, | |
54 | + gfm: true, | |
55 | + breaks: false, | |
56 | + pedantic: false, | |
57 | + smartLists: true, | |
58 | + smartypants: false, | |
59 | + }; | |
60 | +} | |
61 | + | |
62 | +function wrapCopyCode(id: number, content: string, code: string): string { | |
63 | + return '<div class="code-wrapper">' + content + '<span id="copyCodeId' + id + '" style="display: none;">' + code + '</span>' + | |
64 | + '<button id="copyCodeBtn' + id + '" onClick="markdownCopyCode(' + id + ')" ' + | |
65 | + 'class="clipboard-btn"><img src="https://clipboardjs.com/assets/images/clippy.svg" alt="Copy to clipboard">' + | |
66 | + '</button></div>'; | |
67 | +} | |
68 | + | |
69 | +(window as any).markdownCopyCode = (id: number) => { | |
70 | + const text = $('#copyCodeId' + id).text(); | |
71 | + navigator.clipboard.writeText(text).then(() => { | |
72 | + import('tooltipster').then( | |
73 | + () => { | |
74 | + const copyBtn = $('#copyCodeBtn' + id); | |
75 | + if (!copyBtn.hasClass('tooltipstered')) { | |
76 | + copyBtn.tooltipster( | |
77 | + { | |
78 | + content: 'Copied!', | |
79 | + theme: 'tooltipster-shadow', | |
80 | + delay: 0, | |
81 | + trigger: 'custom', | |
82 | + triggerClose: { | |
83 | + click: true, | |
84 | + tap: true, | |
85 | + scroll: true, | |
86 | + mouseleave: true | |
87 | + }, | |
88 | + side: 'bottom', | |
89 | + distance: 12, | |
90 | + trackOrigin: true | |
91 | + } | |
92 | + ); | |
93 | + } | |
94 | + const tooltip = copyBtn.tooltipster('instance'); | |
95 | + tooltip.open(); | |
96 | + } | |
97 | + ); | |
98 | + }); | |
99 | +}; | ... | ... |
... | ... | @@ -14,7 +14,7 @@ |
14 | 14 | /// limitations under the License. |
15 | 15 | /// |
16 | 16 | |
17 | -import { NgModule } from '@angular/core'; | |
17 | +import { NgModule, SecurityContext } from '@angular/core'; | |
18 | 18 | import { CommonModule, DatePipe } from '@angular/common'; |
19 | 19 | import { FooterComponent } from '@shared/components/footer.component'; |
20 | 20 | import { LogoComponent } from '@shared/components/logo.component'; |
... | ... | @@ -78,6 +78,7 @@ import { DatetimePeriodComponent } from '@shared/components/time/datetime-period |
78 | 78 | import { EnumToArrayPipe } from '@shared/pipe/enum-to-array.pipe'; |
79 | 79 | import { ClipboardModule } from 'ngx-clipboard'; |
80 | 80 | import { ValueInputComponent } from '@shared/components/value-input.component'; |
81 | +import { MarkdownModule, MarkedOptions } from 'ngx-markdown'; | |
81 | 82 | import { FullscreenDirective } from '@shared/components/fullscreen.directive'; |
82 | 83 | import { HighlightPipe } from '@shared/pipe/highlight.pipe'; |
83 | 84 | import { DashboardAutocompleteComponent } from '@shared/components/dashboard-autocomplete.component'; |
... | ... | @@ -145,6 +146,7 @@ import { OtaPackageAutocompleteComponent } from '@shared/components/ota-package/ |
145 | 146 | import { MAT_DATE_LOCALE } from '@angular/material/core'; |
146 | 147 | import { CopyButtonComponent } from '@shared/components/button/copy-button.component'; |
147 | 148 | import { TogglePasswordComponent } from '@shared/components/button/toggle-password.component'; |
149 | +import { markedOptionsFactory } from '@shared/components/markdown.factory'; | |
148 | 150 | |
149 | 151 | @NgModule({ |
150 | 152 | providers: [ |
... | ... | @@ -294,7 +296,15 @@ import { TogglePasswordComponent } from '@shared/components/button/toggle-passwo |
294 | 296 | NgxHmCarouselModule, |
295 | 297 | DndModule, |
296 | 298 | NgxFlowModule, |
297 | - NgxFlowchartModule | |
299 | + NgxFlowchartModule, | |
300 | + // ngx-markdown | |
301 | + MarkdownModule.forRoot({ | |
302 | + sanitize: SecurityContext.NONE, | |
303 | + markedOptions: { | |
304 | + provide: MarkedOptions, | |
305 | + useFactory: markedOptionsFactory | |
306 | + } | |
307 | + }) | |
298 | 308 | ], |
299 | 309 | exports: [ |
300 | 310 | FooterComponent, |
... | ... | @@ -386,6 +396,7 @@ import { TogglePasswordComponent } from '@shared/components/button/toggle-passwo |
386 | 396 | NgxHmCarouselModule, |
387 | 397 | DndModule, |
388 | 398 | NgxFlowchartModule, |
399 | + MarkdownModule, | |
389 | 400 | ConfirmDialogComponent, |
390 | 401 | AlertDialogComponent, |
391 | 402 | TodoDialogComponent, | ... | ... |
... | ... | @@ -1817,6 +1817,11 @@ |
1817 | 1817 | resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.170.tgz#0d67711d4bf7f4ca5147e9091b847479b87925d6" |
1818 | 1818 | integrity sha512-bpcvu/MKHHeYX+qeEN8GE7DIravODWdACVA1ctevD8CN24RhPZIKMn9ntfAsrvLfSX3cR5RrBKAbYm9bGs0A+Q== |
1819 | 1819 | |
1820 | +"@types/marked@^1.1.0": | |
1821 | + version "1.2.2" | |
1822 | + resolved "https://registry.yarnpkg.com/@types/marked/-/marked-1.2.2.tgz#1f858a0e690247ecf3b2eef576f98f86e8d960d4" | |
1823 | + integrity sha512-wLfw1hnuuDYrFz97IzJja0pdVsC0oedtS4QsKH1/inyW9qkLQbXgMUqEQT0MVtUBx3twjWeInUfjQbhBVLECXw== | |
1824 | + | |
1820 | 1825 | "@types/minimatch@*": |
1821 | 1826 | version "3.0.3" |
1822 | 1827 | resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" |
... | ... | @@ -4137,6 +4142,11 @@ emoji-regex@^8.0.0: |
4137 | 4142 | resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" |
4138 | 4143 | integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== |
4139 | 4144 | |
4145 | +emoji-toolkit@^6.0.1: | |
4146 | + version "6.6.0" | |
4147 | + resolved "https://registry.yarnpkg.com/emoji-toolkit/-/emoji-toolkit-6.6.0.tgz#e7287c43a96f940ec4c5428cd7100a40e57518f1" | |
4148 | + integrity sha512-pEu0kow2p1N8zCKnn/L6H0F3rWUBB3P3hVjr/O5yl1fK7N9jU4vO4G7EFapC5Y3XwZLUCY0FZbOPyTkH+4V2eQ== | |
4149 | + | |
4140 | 4150 | emojis-list@^3.0.0: |
4141 | 4151 | version "3.0.0" |
4142 | 4152 | resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" |
... | ... | @@ -6126,6 +6136,13 @@ karma@~6.3.2: |
6126 | 6136 | ua-parser-js "^0.7.23" |
6127 | 6137 | yargs "^16.1.1" |
6128 | 6138 | |
6139 | +katex@^0.12.0: | |
6140 | + version "0.12.0" | |
6141 | + resolved "https://registry.yarnpkg.com/katex/-/katex-0.12.0.tgz#2fb1c665dbd2b043edcf8a1f5c555f46beaa0cb9" | |
6142 | + integrity sha512-y+8btoc/CK70XqcHqjxiGWBOeIL8upbS0peTPXTvgrh21n1RiWWcIpSWM+4uXq+IAgNh9YYQWdc7LVDPDAEEAg== | |
6143 | + dependencies: | |
6144 | + commander "^2.19.0" | |
6145 | + | |
6129 | 6146 | killable@^1.0.1: |
6130 | 6147 | version "1.0.1" |
6131 | 6148 | resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892" |
... | ... | @@ -6425,6 +6442,11 @@ map-visit@^1.0.0: |
6425 | 6442 | dependencies: |
6426 | 6443 | object-visit "^1.0.0" |
6427 | 6444 | |
6445 | +marked@^1.1.0: | |
6446 | + version "1.2.9" | |
6447 | + resolved "https://registry.yarnpkg.com/marked/-/marked-1.2.9.tgz#53786f8b05d4c01a2a5a76b7d1ec9943d29d72dc" | |
6448 | + integrity sha512-H8lIX2SvyitGX+TRdtS06m1jHMijKN/XjfH6Ooii9fvxMlh8QdqBfBDkGUpMWH2kQNrtixjzYUa3SH8ROTgRRw== | |
6449 | + | |
6428 | 6450 | material-design-icons@^3.0.1: |
6429 | 6451 | version "3.0.1" |
6430 | 6452 | resolved "https://registry.yarnpkg.com/material-design-icons/-/material-design-icons-3.0.1.tgz#9a71c48747218ebca51e51a66da682038cdcb7bf" |
... | ... | @@ -6877,6 +6899,18 @@ ngx-hm-carousel@^2.0.0-rc.1: |
6877 | 6899 | hammerjs "^2.0.8" |
6878 | 6900 | resize-observer-polyfill "^1.5.1" |
6879 | 6901 | |
6902 | +ngx-markdown@^10.1.1: | |
6903 | + version "10.1.1" | |
6904 | + resolved "https://registry.yarnpkg.com/ngx-markdown/-/ngx-markdown-10.1.1.tgz#17840c773db7ced4b18ccbf2e8cb06182e422de3" | |
6905 | + integrity sha512-bUVgN6asb35d5U4xM5CNfo7pSpuwqJSdTgK0PhNZzLiaiyPIK2owtLF6sWGhxTThJu+LngJPjj4MQ+AFe/s8XQ== | |
6906 | + dependencies: | |
6907 | + "@types/marked" "^1.1.0" | |
6908 | + emoji-toolkit "^6.0.1" | |
6909 | + katex "^0.12.0" | |
6910 | + marked "^1.1.0" | |
6911 | + prismjs "^1.20.0" | |
6912 | + tslib "^2.0.0" | |
6913 | + | |
6880 | 6914 | ngx-sharebuttons@^8.0.5: |
6881 | 6915 | version "8.0.5" |
6882 | 6916 | resolved "https://registry.yarnpkg.com/ngx-sharebuttons/-/ngx-sharebuttons-8.0.5.tgz#49481fcb8bf9541747fd72093eca6f4777c1d371" |
... | ... | @@ -7877,6 +7911,11 @@ pretty-bytes@^5.3.0: |
7877 | 7911 | resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" |
7878 | 7912 | integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== |
7879 | 7913 | |
7914 | +prismjs@^1.20.0: | |
7915 | + version "1.24.1" | |
7916 | + resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.24.1.tgz#c4d7895c4d6500289482fa8936d9cdd192684036" | |
7917 | + integrity sha512-mNPsedLuk90RVJioIky8ANZEwYm5w9LcvCXrxHlwf4fNVSn8jEipMybMkWUyyF0JhnC+C4VcOVSBuHRKs1L5Ow== | |
7918 | + | |
7880 | 7919 | prismjs@^1.23.0: |
7881 | 7920 | version "1.23.0" |
7882 | 7921 | resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.23.0.tgz#d3b3967f7d72440690497652a9d40ff046067f33" | ... | ... |