Add markdown/HTML widget
This commit is contained in:
parent
327607e86d
commit
9c1a2a4cb1
File diff suppressed because one or more lines are too long
@ -78,7 +78,8 @@
|
||||
"node_modules/leaflet/dist/leaflet.css",
|
||||
"src/app/modules/home/components/widget/lib/maps/markers.scss",
|
||||
"node_modules/leaflet.markercluster/dist/MarkerCluster.css",
|
||||
"node_modules/leaflet.markercluster/dist/MarkerCluster.Default.css"
|
||||
"node_modules/leaflet.markercluster/dist/MarkerCluster.Default.css",
|
||||
"node_modules/prismjs/themes/prism.css"
|
||||
],
|
||||
"stylePreprocessorOptions": {
|
||||
"includePaths": [
|
||||
@ -88,7 +89,11 @@
|
||||
"scripts": [
|
||||
"node_modules/tinycolor2/dist/tinycolor-min.js",
|
||||
"node_modules/split.js/dist/split.min.js",
|
||||
"node_modules/systemjs/dist/system.js"
|
||||
"node_modules/systemjs/dist/system.js",
|
||||
"node_modules/marked/lib/marked.js",
|
||||
"node_modules/prismjs/prism.js",
|
||||
"node_modules/prismjs/components/prism-bash.min.js",
|
||||
"node_modules/prismjs/components/prism-json.min.js"
|
||||
],
|
||||
"customWebpackConfig": {
|
||||
"path": "./extra-webpack.config.js"
|
||||
|
||||
@ -72,6 +72,7 @@
|
||||
"ngx-drag-drop": "^2.0.0",
|
||||
"ngx-flowchart": "git://github.com/thingsboard/ngx-flowchart.git#master",
|
||||
"ngx-hm-carousel": "^2.0.0-rc.1",
|
||||
"ngx-markdown": "^10.1.1",
|
||||
"ngx-sharebuttons": "^8.0.5",
|
||||
"ngx-translate-messageformat-compiler": "^4.9.0",
|
||||
"objectpath": "^2.0.0",
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
|
||||
import { FormattedData, MapProviders, ReplaceInfo } from '@home/components/widget/lib/maps/map-models';
|
||||
import {
|
||||
createLabelFromDatasource,
|
||||
createLabelFromDatasource, deepClone,
|
||||
hashCode,
|
||||
isDefined,
|
||||
isDefinedAndNotNull,
|
||||
@ -343,6 +343,22 @@ export function parseData(input: DatasourceData[]): FormattedData[] {
|
||||
});
|
||||
}
|
||||
|
||||
export function flatData(input: FormattedData[]): FormattedData {
|
||||
let result: FormattedData = {} as FormattedData;
|
||||
if (input.length) {
|
||||
for (const toMerge of input) {
|
||||
result = {...result, ...toMerge};
|
||||
}
|
||||
result.entityName = input[0].entityName;
|
||||
result.entityId = input[0].entityId;
|
||||
result.entityType = input[0].entityType;
|
||||
result.$datasource = input[0].$datasource;
|
||||
result.dsIndex = input[0].dsIndex;
|
||||
result.deviceType = input[0].deviceType;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function parseArray(input: DatasourceData[]): FormattedData[][] {
|
||||
return _(input).groupBy(el => el?.datasource?.entityName)
|
||||
.values().value().map((entityArray) =>
|
||||
|
||||
@ -0,0 +1,18 @@
|
||||
<!--
|
||||
|
||||
Copyright © 2016-2021 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.
|
||||
|
||||
-->
|
||||
<markdown [data]="markdownText" class="tb-markdown-view" (click)="markdownClick($event)"></markdown>
|
||||
@ -0,0 +1,130 @@
|
||||
/**
|
||||
* Copyright © 2016-2021 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.
|
||||
*/
|
||||
|
||||
.tb-markdown-view {
|
||||
display: block;
|
||||
::ng-deep {
|
||||
h1 {
|
||||
font-size: 32px;
|
||||
padding-right: 60px;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
line-height: normal;
|
||||
font-weight: 500;
|
||||
margin-bottom: 30px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
line-height: 1.25em;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
p+p {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border: 1px solid #ccc;
|
||||
border-spacing: 0;
|
||||
margin-top: 30px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
thead {
|
||||
background-color: #555;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
th, td {
|
||||
font-size: .85em;
|
||||
padding: 8px;
|
||||
margin: 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
td[align=center], th[align=center] {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
td[align=right], th[align=right] {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
tr:nth-child(even) {
|
||||
background-color: #f7f7f7;
|
||||
}
|
||||
|
||||
code:not([class*=language-]) {
|
||||
background: #f5f5f5;
|
||||
border-radius: 2px;
|
||||
color: #dd4a68;
|
||||
padding: 2px 4px;
|
||||
}
|
||||
|
||||
div.code-wrapper {
|
||||
position: relative;
|
||||
button.clipboard-btn {
|
||||
cursor: pointer;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
outline: none;
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 5px;
|
||||
background: #fff;
|
||||
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);
|
||||
border-radius: 5px;
|
||||
opacity: 0;
|
||||
transition: opacity .3s;
|
||||
padding: 3px 6px;
|
||||
line-height: 16px;
|
||||
img {
|
||||
width: 18px;
|
||||
}
|
||||
&:hover {
|
||||
background: #f9f9f9;
|
||||
}
|
||||
&:active {
|
||||
background-color: #ececec;
|
||||
box-shadow: inset 1px -1px 4px 0px rgba(0,0,0,0.4);
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
button.clipboard-btn {
|
||||
opacity: .85;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
th, td {
|
||||
div.code-wrapper {
|
||||
display: inline-block;
|
||||
button.clipboard-btn {
|
||||
top: -5px;
|
||||
right: -30px;
|
||||
padding: 0 3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,116 @@
|
||||
///
|
||||
/// Copyright © 2016-2021 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 { ChangeDetectorRef, Component, ElementRef, Input, OnInit } from '@angular/core';
|
||||
import { PageComponent } from '@shared/components/page.component';
|
||||
import { WidgetContext } from '@home/models/widget-component.models';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { AppState } from '@core/core.state';
|
||||
import { DatasourceData } from '@shared/models/widget.models';
|
||||
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
|
||||
import {
|
||||
fillPattern, flatData,
|
||||
parseData,
|
||||
parseFunction,
|
||||
processPattern,
|
||||
safeExecute
|
||||
} from '@home/components/widget/lib/maps/common-maps-utils';
|
||||
import { FormattedData } from '@home/components/widget/lib/maps/map-models';
|
||||
import { hashCode, isNotEmptyStr } from '@core/utils';
|
||||
import cssjs from '@core/css/css';
|
||||
|
||||
interface MarkdownWidgetSettings {
|
||||
markdownTextPattern: string;
|
||||
useMarkdownTextFunction: boolean;
|
||||
markdownTextFunction: string;
|
||||
markdownCss: string;
|
||||
}
|
||||
|
||||
type MarkdownTextFunction = (data: FormattedData[]) => string;
|
||||
|
||||
@Component({
|
||||
selector: 'tb-markdown-widget ',
|
||||
templateUrl: './markdown-widget.component.html',
|
||||
styleUrls: ['./markdown-widget.component.scss']
|
||||
})
|
||||
export class MarkdownWidgetComponent extends PageComponent implements OnInit {
|
||||
|
||||
settings: MarkdownWidgetSettings;
|
||||
markdownTextFunction: MarkdownTextFunction;
|
||||
|
||||
@Input()
|
||||
ctx: WidgetContext;
|
||||
|
||||
markdownText: string;
|
||||
|
||||
constructor(protected store: Store<AppState>,
|
||||
private elementRef: ElementRef,
|
||||
private cd: ChangeDetectorRef) {
|
||||
super(store);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.ctx.$scope.markdownWidget = this;
|
||||
this.settings = this.ctx.settings;
|
||||
this.markdownTextFunction = this.settings.useMarkdownTextFunction ? parseFunction(this.settings.markdownTextFunction, ['data']) : null;
|
||||
|
||||
const cssString = this.settings.markdownCss;
|
||||
if (isNotEmptyStr(cssString)) {
|
||||
const cssParser = new cssjs();
|
||||
cssParser.testMode = false;
|
||||
const namespace = 'entities-hierarchy-' + hashCode(cssString);
|
||||
cssParser.cssPreviewNamespace = namespace;
|
||||
cssParser.createStyleElement(namespace, cssString);
|
||||
$(this.elementRef.nativeElement).addClass(namespace);
|
||||
}
|
||||
}
|
||||
|
||||
public onDataUpdated() {
|
||||
let initialData: DatasourceData[];
|
||||
if (this.ctx.data?.length) {
|
||||
initialData = this.ctx.data;
|
||||
} else if (this.ctx.datasources?.length) {
|
||||
initialData = [
|
||||
{
|
||||
datasource: this.ctx.datasources[0],
|
||||
dataKey: {
|
||||
type: DataKeyType.attribute,
|
||||
name: 'empty'
|
||||
},
|
||||
data: []
|
||||
}
|
||||
];
|
||||
}
|
||||
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);
|
||||
}
|
||||
if (this.markdownText !== markdownText) {
|
||||
this.markdownText = markdownText;
|
||||
this.cd.detectChanges();
|
||||
}
|
||||
}
|
||||
|
||||
markdownClick($event: MouseEvent) {
|
||||
this.ctx.actionsApi.elementClick($event);
|
||||
}
|
||||
|
||||
}
|
||||
@ -101,7 +101,7 @@ export class QrCodeWidgetComponent extends PageComponent implements OnInit, Afte
|
||||
const dataSourceData = data[0];
|
||||
const pattern = this.settings.useQrCodeTextFunction ?
|
||||
safeExecute(this.qrCodeTextFunction, [dataSourceData]) : this.settings.qrCodeTextPattern;
|
||||
const replaceInfo = processPattern(pattern, data);
|
||||
const replaceInfo = processPattern(pattern, dataSourceData);
|
||||
qrCodeText = fillPattern(pattern, replaceInfo, dataSourceData);
|
||||
}
|
||||
this.updateQrCodeText(qrCodeText);
|
||||
|
||||
@ -40,6 +40,7 @@ import { NavigationCardWidgetComponent } from '@home/components/widget/lib/navig
|
||||
import { EdgesOverviewWidgetComponent } from '@home/components/widget/lib/edges-overview-widget.component';
|
||||
import { JsonInputWidgetComponent } from '@home/components/widget/lib/json-input-widget.component';
|
||||
import { QrCodeWidgetComponent } from '@home/components/widget/lib/qrcode-widget.component';
|
||||
import { MarkdownWidgetComponent } from '@home/components/widget/lib/markdown-widget.component';
|
||||
|
||||
@NgModule({
|
||||
declarations:
|
||||
@ -60,7 +61,8 @@ import { QrCodeWidgetComponent } from '@home/components/widget/lib/qrcode-widget
|
||||
GatewayFormComponent,
|
||||
NavigationCardsWidgetComponent,
|
||||
NavigationCardWidgetComponent,
|
||||
QrCodeWidgetComponent
|
||||
QrCodeWidgetComponent,
|
||||
MarkdownWidgetComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
@ -83,7 +85,8 @@ import { QrCodeWidgetComponent } from '@home/components/widget/lib/qrcode-widget
|
||||
GatewayFormComponent,
|
||||
NavigationCardsWidgetComponent,
|
||||
NavigationCardWidgetComponent,
|
||||
QrCodeWidgetComponent
|
||||
QrCodeWidgetComponent,
|
||||
MarkdownWidgetComponent
|
||||
],
|
||||
providers: [
|
||||
CustomDialogService,
|
||||
|
||||
@ -159,8 +159,8 @@ class ThingsboardAceEditor extends React.Component<ThingsboardAceEditorProps, Th
|
||||
<div className='json-form-ace-editor'>
|
||||
<div className='title-panel'>
|
||||
<label>{this.props.mode}</label>
|
||||
<Button style={ styles.tidyButtonStyle }
|
||||
className='tidy-button' onClick={this.onTidy}>Tidy</Button>
|
||||
{ this.props.onTidy ? <Button style={ styles.tidyButtonStyle }
|
||||
className='tidy-button' onClick={this.onTidy}>Tidy</Button> : null }
|
||||
<Button style={ styles.tidyButtonStyle }
|
||||
className='tidy-button' onClick={this.onToggleFull}>
|
||||
{this.state.isFull ?
|
||||
|
||||
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright © 2016-2021 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 React from 'react';
|
||||
import ThingsboardAceEditor from './json-form-ace-editor';
|
||||
import { JsonFormFieldProps, JsonFormFieldState } from '@shared/components/json-form/react/json-form.models';
|
||||
|
||||
class ThingsboardMarkdown extends React.Component<JsonFormFieldProps, JsonFormFieldState> {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<ThingsboardAceEditor {...this.props} mode='markdown' {...this.state}></ThingsboardAceEditor>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ThingsboardMarkdown;
|
||||
@ -38,6 +38,7 @@ import { JsonFormData, JsonFormProps, onChangeFn, OnColorClickFn, OnIconClickFn
|
||||
import _ from 'lodash';
|
||||
import * as tinycolor_ from 'tinycolor2';
|
||||
import { GroupInfo } from '@shared/models/widget.models';
|
||||
import ThingsboardMarkdown from '@shared/components/json-form/react/json-form-markdown';
|
||||
|
||||
const tinycolor = tinycolor_;
|
||||
|
||||
@ -65,6 +66,7 @@ class ThingsboardSchemaForm extends React.Component<JsonFormProps, any> {
|
||||
json: ThingsboardJson,
|
||||
html: ThingsboardHtml,
|
||||
css: ThingsboardCss,
|
||||
markdown: ThingsboardMarkdown,
|
||||
color: ThingsboardColor,
|
||||
'rc-select': ThingsboardRcSelect,
|
||||
fieldset: ThingsboardFieldSet,
|
||||
@ -91,7 +93,7 @@ class ThingsboardSchemaForm extends React.Component<JsonFormProps, any> {
|
||||
}
|
||||
|
||||
onIconClick(key: (string | number)[], val: string,
|
||||
iconSelectedFn: (icon: string) => void) {
|
||||
iconSelectedFn: (icon: string) => void) {
|
||||
this.props.onIconClick(key, val, iconSelectedFn);
|
||||
}
|
||||
|
||||
|
||||
99
ui-ngx/src/app/shared/components/markdown.factory.ts
Normal file
99
ui-ngx/src/app/shared/components/markdown.factory.ts
Normal file
@ -0,0 +1,99 @@
|
||||
///
|
||||
/// Copyright © 2016-2021 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 { MarkedOptions, MarkedRenderer } from 'ngx-markdown';
|
||||
|
||||
export function markedOptionsFactory(): MarkedOptions {
|
||||
const renderer = new MarkedRenderer();
|
||||
const renderer2 = new MarkedRenderer();
|
||||
|
||||
const copyCodeBlock = '{:copy-code}';
|
||||
|
||||
let id = 1;
|
||||
|
||||
renderer.code = (code: string, language: string | undefined, isEscaped: boolean) => {
|
||||
if (code.endsWith(copyCodeBlock)) {
|
||||
code = code.substring(0, code.length - copyCodeBlock.length);
|
||||
const content = renderer2.code(code, language, isEscaped);
|
||||
id++;
|
||||
return wrapCopyCode(id, content, code);
|
||||
} else {
|
||||
return renderer2.code(code, language, isEscaped);
|
||||
}
|
||||
};
|
||||
|
||||
renderer.tablecell = (content: string, flags: {
|
||||
header: boolean;
|
||||
align: 'center' | 'left' | 'right' | null;
|
||||
}) => {
|
||||
if (content.endsWith(copyCodeBlock)) {
|
||||
content = content.substring(0, content.length - copyCodeBlock.length);
|
||||
id++;
|
||||
content = wrapCopyCode(id, content, content);
|
||||
}
|
||||
return renderer2.tablecell(content, flags);
|
||||
};
|
||||
|
||||
return {
|
||||
renderer,
|
||||
headerIds: true,
|
||||
gfm: true,
|
||||
breaks: false,
|
||||
pedantic: false,
|
||||
smartLists: true,
|
||||
smartypants: false,
|
||||
};
|
||||
}
|
||||
|
||||
function wrapCopyCode(id: number, content: string, code: string): string {
|
||||
return '<div class="code-wrapper">' + content + '<span id="copyCodeId' + id + '" style="display: none;">' + code + '</span>' +
|
||||
'<button id="copyCodeBtn' + id + '" onClick="markdownCopyCode(' + id + ')" ' +
|
||||
'class="clipboard-btn"><img src="https://clipboardjs.com/assets/images/clippy.svg" alt="Copy to clipboard">' +
|
||||
'</button></div>';
|
||||
}
|
||||
|
||||
(window as any).markdownCopyCode = (id: number) => {
|
||||
const text = $('#copyCodeId' + id).text();
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
import('tooltipster').then(
|
||||
() => {
|
||||
const copyBtn = $('#copyCodeBtn' + id);
|
||||
if (!copyBtn.hasClass('tooltipstered')) {
|
||||
copyBtn.tooltipster(
|
||||
{
|
||||
content: 'Copied!',
|
||||
theme: 'tooltipster-shadow',
|
||||
delay: 0,
|
||||
trigger: 'custom',
|
||||
triggerClose: {
|
||||
click: true,
|
||||
tap: true,
|
||||
scroll: true,
|
||||
mouseleave: true
|
||||
},
|
||||
side: 'bottom',
|
||||
distance: 12,
|
||||
trackOrigin: true
|
||||
}
|
||||
);
|
||||
}
|
||||
const tooltip = copyBtn.tooltipster('instance');
|
||||
tooltip.open();
|
||||
}
|
||||
);
|
||||
});
|
||||
};
|
||||
@ -14,7 +14,7 @@
|
||||
/// limitations under the License.
|
||||
///
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { NgModule, SecurityContext } from '@angular/core';
|
||||
import { CommonModule, DatePipe } from '@angular/common';
|
||||
import { FooterComponent } from '@shared/components/footer.component';
|
||||
import { LogoComponent } from '@shared/components/logo.component';
|
||||
@ -78,6 +78,7 @@ import { DatetimePeriodComponent } from '@shared/components/time/datetime-period
|
||||
import { EnumToArrayPipe } from '@shared/pipe/enum-to-array.pipe';
|
||||
import { ClipboardModule } from 'ngx-clipboard';
|
||||
import { ValueInputComponent } from '@shared/components/value-input.component';
|
||||
import { MarkdownModule, MarkedOptions } from 'ngx-markdown';
|
||||
import { FullscreenDirective } from '@shared/components/fullscreen.directive';
|
||||
import { HighlightPipe } from '@shared/pipe/highlight.pipe';
|
||||
import { DashboardAutocompleteComponent } from '@shared/components/dashboard-autocomplete.component';
|
||||
@ -145,6 +146,7 @@ import { OtaPackageAutocompleteComponent } from '@shared/components/ota-package/
|
||||
import { MAT_DATE_LOCALE } from '@angular/material/core';
|
||||
import { CopyButtonComponent } from '@shared/components/button/copy-button.component';
|
||||
import { TogglePasswordComponent } from '@shared/components/button/toggle-password.component';
|
||||
import { markedOptionsFactory } from '@shared/components/markdown.factory';
|
||||
|
||||
@NgModule({
|
||||
providers: [
|
||||
@ -294,7 +296,15 @@ import { TogglePasswordComponent } from '@shared/components/button/toggle-passwo
|
||||
NgxHmCarouselModule,
|
||||
DndModule,
|
||||
NgxFlowModule,
|
||||
NgxFlowchartModule
|
||||
NgxFlowchartModule,
|
||||
// ngx-markdown
|
||||
MarkdownModule.forRoot({
|
||||
sanitize: SecurityContext.NONE,
|
||||
markedOptions: {
|
||||
provide: MarkedOptions,
|
||||
useFactory: markedOptionsFactory
|
||||
}
|
||||
})
|
||||
],
|
||||
exports: [
|
||||
FooterComponent,
|
||||
@ -386,6 +396,7 @@ import { TogglePasswordComponent } from '@shared/components/button/toggle-passwo
|
||||
NgxHmCarouselModule,
|
||||
DndModule,
|
||||
NgxFlowchartModule,
|
||||
MarkdownModule,
|
||||
ConfirmDialogComponent,
|
||||
AlertDialogComponent,
|
||||
TodoDialogComponent,
|
||||
|
||||
@ -1817,6 +1817,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.170.tgz#0d67711d4bf7f4ca5147e9091b847479b87925d6"
|
||||
integrity sha512-bpcvu/MKHHeYX+qeEN8GE7DIravODWdACVA1ctevD8CN24RhPZIKMn9ntfAsrvLfSX3cR5RrBKAbYm9bGs0A+Q==
|
||||
|
||||
"@types/marked@^1.1.0":
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/marked/-/marked-1.2.2.tgz#1f858a0e690247ecf3b2eef576f98f86e8d960d4"
|
||||
integrity sha512-wLfw1hnuuDYrFz97IzJja0pdVsC0oedtS4QsKH1/inyW9qkLQbXgMUqEQT0MVtUBx3twjWeInUfjQbhBVLECXw==
|
||||
|
||||
"@types/minimatch@*":
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
|
||||
@ -4137,6 +4142,11 @@ emoji-regex@^8.0.0:
|
||||
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
|
||||
integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
|
||||
|
||||
emoji-toolkit@^6.0.1:
|
||||
version "6.6.0"
|
||||
resolved "https://registry.yarnpkg.com/emoji-toolkit/-/emoji-toolkit-6.6.0.tgz#e7287c43a96f940ec4c5428cd7100a40e57518f1"
|
||||
integrity sha512-pEu0kow2p1N8zCKnn/L6H0F3rWUBB3P3hVjr/O5yl1fK7N9jU4vO4G7EFapC5Y3XwZLUCY0FZbOPyTkH+4V2eQ==
|
||||
|
||||
emojis-list@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78"
|
||||
@ -6126,6 +6136,13 @@ karma@~6.3.2:
|
||||
ua-parser-js "^0.7.23"
|
||||
yargs "^16.1.1"
|
||||
|
||||
katex@^0.12.0:
|
||||
version "0.12.0"
|
||||
resolved "https://registry.yarnpkg.com/katex/-/katex-0.12.0.tgz#2fb1c665dbd2b043edcf8a1f5c555f46beaa0cb9"
|
||||
integrity sha512-y+8btoc/CK70XqcHqjxiGWBOeIL8upbS0peTPXTvgrh21n1RiWWcIpSWM+4uXq+IAgNh9YYQWdc7LVDPDAEEAg==
|
||||
dependencies:
|
||||
commander "^2.19.0"
|
||||
|
||||
killable@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892"
|
||||
@ -6425,6 +6442,11 @@ map-visit@^1.0.0:
|
||||
dependencies:
|
||||
object-visit "^1.0.0"
|
||||
|
||||
marked@^1.1.0:
|
||||
version "1.2.9"
|
||||
resolved "https://registry.yarnpkg.com/marked/-/marked-1.2.9.tgz#53786f8b05d4c01a2a5a76b7d1ec9943d29d72dc"
|
||||
integrity sha512-H8lIX2SvyitGX+TRdtS06m1jHMijKN/XjfH6Ooii9fvxMlh8QdqBfBDkGUpMWH2kQNrtixjzYUa3SH8ROTgRRw==
|
||||
|
||||
material-design-icons@^3.0.1:
|
||||
version "3.0.1"
|
||||
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:
|
||||
hammerjs "^2.0.8"
|
||||
resize-observer-polyfill "^1.5.1"
|
||||
|
||||
ngx-markdown@^10.1.1:
|
||||
version "10.1.1"
|
||||
resolved "https://registry.yarnpkg.com/ngx-markdown/-/ngx-markdown-10.1.1.tgz#17840c773db7ced4b18ccbf2e8cb06182e422de3"
|
||||
integrity sha512-bUVgN6asb35d5U4xM5CNfo7pSpuwqJSdTgK0PhNZzLiaiyPIK2owtLF6sWGhxTThJu+LngJPjj4MQ+AFe/s8XQ==
|
||||
dependencies:
|
||||
"@types/marked" "^1.1.0"
|
||||
emoji-toolkit "^6.0.1"
|
||||
katex "^0.12.0"
|
||||
marked "^1.1.0"
|
||||
prismjs "^1.20.0"
|
||||
tslib "^2.0.0"
|
||||
|
||||
ngx-sharebuttons@^8.0.5:
|
||||
version "8.0.5"
|
||||
resolved "https://registry.yarnpkg.com/ngx-sharebuttons/-/ngx-sharebuttons-8.0.5.tgz#49481fcb8bf9541747fd72093eca6f4777c1d371"
|
||||
@ -7877,6 +7911,11 @@ pretty-bytes@^5.3.0:
|
||||
resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb"
|
||||
integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==
|
||||
|
||||
prismjs@^1.20.0:
|
||||
version "1.24.1"
|
||||
resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.24.1.tgz#c4d7895c4d6500289482fa8936d9cdd192684036"
|
||||
integrity sha512-mNPsedLuk90RVJioIky8ANZEwYm5w9LcvCXrxHlwf4fNVSn8jEipMybMkWUyyF0JhnC+C4VcOVSBuHRKs1L5Ow==
|
||||
|
||||
prismjs@^1.23.0:
|
||||
version "1.23.0"
|
||||
resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.23.0.tgz#d3b3967f7d72440690497652a9d40ff046067f33"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user