Browse Source

Add markdown/HTML widget

pull/5121/head
Igor Kulikov 5 years ago
parent
commit
9c1a2a4cb1
  1. 18
      application/src/main/data/json/system/widget_bundles/cards.json
  2. 9
      ui-ngx/angular.json
  3. 1
      ui-ngx/package.json
  4. 18
      ui-ngx/src/app/modules/home/components/widget/lib/maps/common-maps-utils.ts
  5. 18
      ui-ngx/src/app/modules/home/components/widget/lib/markdown-widget.component.html
  6. 130
      ui-ngx/src/app/modules/home/components/widget/lib/markdown-widget.component.scss
  7. 116
      ui-ngx/src/app/modules/home/components/widget/lib/markdown-widget.component.ts
  8. 2
      ui-ngx/src/app/modules/home/components/widget/lib/qrcode-widget.component.ts
  9. 7
      ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts
  10. 4
      ui-ngx/src/app/shared/components/json-form/react/json-form-ace-editor.tsx
  11. 33
      ui-ngx/src/app/shared/components/json-form/react/json-form-markdown.tsx
  12. 4
      ui-ngx/src/app/shared/components/json-form/react/json-form-schema-form.tsx
  13. 99
      ui-ngx/src/app/shared/components/markdown.factory.ts
  14. 15
      ui-ngx/src/app/shared/shared.module.ts
  15. 39
      ui-ngx/yarn.lock

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

File diff suppressed because one or more lines are too long

9
ui-ngx/angular.json

@ -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"

1
ui-ngx/package.json

@ -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",

18
ui-ngx/src/app/modules/home/components/widget/lib/maps/common-maps-utils.ts

@ -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) =>

18
ui-ngx/src/app/modules/home/components/widget/lib/markdown-widget.component.html

@ -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>

130
ui-ngx/src/app/modules/home/components/widget/lib/markdown-widget.component.scss

@ -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;
}
}
}
}
}

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

@ -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);
}
}

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

@ -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);

7
ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts

@ -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,

4
ui-ngx/src/app/shared/components/json-form/react/json-form-ace-editor.tsx

@ -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 ?

33
ui-ngx/src/app/shared/components/json-form/react/json-form-markdown.tsx

@ -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;

4
ui-ngx/src/app/shared/components/json-form/react/json-form-schema-form.tsx

@ -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

@ -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();
}
);
});
};

15
ui-ngx/src/app/shared/shared.module.ts

@ -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,

39
ui-ngx/yarn.lock

@ -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…
Cancel
Save