Browse Source

UI: markdown component and markdown widget improvements

pull/8203/head
Igor Kulikov 4 years ago
parent
commit
e4671e10a6
  1. 1
      ui-ngx/src/app/modules/home/components/widget/lib/markdown-widget.component.html
  2. 7
      ui-ngx/src/app/modules/home/components/widget/lib/markdown-widget.component.ts
  3. 3
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/markdown-widget-settings.component.html
  4. 2
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/markdown-widget-settings.component.ts
  5. 44
      ui-ngx/src/app/shared/components/markdown.component.scss
  6. 56
      ui-ngx/src/app/shared/components/markdown.component.ts
  7. 1
      ui-ngx/src/assets/locale/locale.constant-en_US.json
  8. 49
      ui-ngx/src/styles.scss

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

@ -18,4 +18,5 @@
<tb-markdown [data]="markdownText" [additionalCompileModules]="[ homeComponentsModule ]"
[additionalStyles]="additionalStyles"
[containerClass]="markdownClass"
[applyDefaultMarkdownStyle]="applyDefaultMarkdownStyle"
[context]="{ ctx: ctx }" lineNumbers fallbackToPlainMarkdown (click)="markdownClick($event)"></tb-markdown>

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

@ -40,13 +40,14 @@ interface MarkdownWidgetSettings {
markdownTextPattern: string;
useMarkdownTextFunction: boolean;
markdownTextFunction: string;
applyDefaultMarkdownStyle: boolean;
markdownCss: string;
}
type MarkdownTextFunction = (data: FormattedData[], ctx: WidgetContext) => string;
@Component({
selector: 'tb-markdown-widget ',
selector: 'tb-markdown-widget',
templateUrl: './markdown-widget.component.html'
})
export class MarkdownWidgetComponent extends PageComponent implements OnInit {
@ -63,6 +64,7 @@ export class MarkdownWidgetComponent extends PageComponent implements OnInit {
additionalStyles: string[];
applyDefaultMarkdownStyle = true;
constructor(protected store: Store<AppState>,
private utils: UtilsService,
@ -86,6 +88,9 @@ export class MarkdownWidgetComponent extends PageComponent implements OnInit {
cssString = cssParser.getCSSForEditor(cssString);
this.additionalStyles = [cssString];
}
if (isDefinedAndNotNull(this.settings.applyDefaultMarkdownStyle)) {
this.applyDefaultMarkdownStyle = this.settings.applyDefaultMarkdownStyle;
}
const pageSize = isDefinedAndNotNull(this.ctx.widgetConfig.pageSize) &&
this.ctx.widgetConfig.pageSize > 0 ? this.ctx.widgetConfig.pageSize : 16384;
const pageLink: EntityDataPageLink = {

3
ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/markdown-widget-settings.component.html

@ -31,6 +31,9 @@
label="{{ 'widgets.markdown.markdown-text-pattern' | translate }}"
helpId="widget/editor/widget_js_markdown_pattern">
</tb-markdown-editor>
<mat-slide-toggle class="mat-slide" formControlName="applyDefaultMarkdownStyle">
{{ 'widgets.markdown.apply-default-markdown-style' | translate }}
</mat-slide-toggle>
<tb-css formControlName="markdownCss"
label="{{ 'widgets.markdown.markdown-css' | translate }}">
</tb-css>

2
ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/markdown-widget-settings.component.ts

@ -43,6 +43,7 @@ export class MarkdownWidgetSettingsComponent extends WidgetSettingsComponent {
useMarkdownTextFunction: false,
markdownTextPattern: '# Markdown/HTML card \\n - **Current entity**: **${entityName}**. \\n - **Current value**: **${Random}**.',
markdownTextFunction: 'return \'# Some title\\\\n - Entity name: \' + data[0][\'entityName\'];',
applyDefaultMarkdownStyle: true,
markdownCss: ''
};
}
@ -52,6 +53,7 @@ export class MarkdownWidgetSettingsComponent extends WidgetSettingsComponent {
useMarkdownTextFunction: [settings.useMarkdownTextFunction, []],
markdownTextPattern: [settings.markdownTextPattern, []],
markdownTextFunction: [settings.markdownTextFunction, []],
applyDefaultMarkdownStyle: [settings.applyDefaultMarkdownStyle, []],
markdownCss: [settings.markdownCss, []]
});
}

44
ui-ngx/src/app/shared/components/markdown.component.scss

@ -357,6 +357,50 @@
background: 0 0;
padding: 8px 16px;
color: #212529;
.token.atrule, .token.attr-value, .token.keyword {
color: #2a7dec;
}
.token.selector, .token.attr-name, .token.string, .token.char, .token.builtin, .token.inserted {
color: #eb5757;
}
.token.punctuation {
color: #212529;
}
&.line-numbers {
padding-left: 66px;
& > code {
span.line-numbers-rows {
top: -12px;
bottom: -12px;
left: -66px;
width: 50px;
border: none;
padding: 8px 12px 8px 18px;
text-align: right;
background: #f9fbff;
& > span:before {
color: rgba(33, 37, 41, .6);
padding-right: 0;
}
}
}
&.no-line-numbers {
padding-left: 16px;
& > code {
span.line-numbers-rows {
display: none;
}
}
}
}
}
}
}

56
ui-ngx/src/app/shared/components/markdown.component.ts

@ -26,6 +26,7 @@ import {
Input,
OnChanges,
Output,
Renderer2,
SimpleChanges,
Type,
ViewChild,
@ -36,9 +37,11 @@ import { MarkdownService, PrismPlugin } from 'ngx-markdown';
import { DynamicComponentFactoryService } from '@core/services/dynamic-component-factory.service';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { SHARED_MODULE_TOKEN } from '@shared/components/tokens';
import { deepClone, isDefinedAndNotNull } from '@core/utils';
import { deepClone, guid, isDefinedAndNotNull } from '@core/utils';
import { Observable, of, ReplaySubject } from 'rxjs';
let defaultMarkdownStyle;
@Component({
selector: 'tb-markdown',
templateUrl: './markdown.component.html',
@ -61,6 +64,8 @@ export class TbMarkdownComponent implements OnChanges {
@Input() style: { [klass: string]: any } = {};
@Input() applyDefaultMarkdownStyle = true;
@Input() additionalStyles: string[];
@Input()
@ -87,7 +92,8 @@ export class TbMarkdownComponent implements OnChanges {
private cd: ChangeDetectorRef,
public markdownService: MarkdownService,
@Inject(SHARED_MODULE_TOKEN) private sharedModule: Type<any>,
private dynamicComponentFactoryService: DynamicComponentFactoryService) {}
private dynamicComponentFactoryService: DynamicComponentFactoryService,
private renderer: Renderer2) {}
ngOnChanges(changes: SimpleChanges): void {
if (isDefinedAndNotNull(this.data)) {
@ -97,15 +103,25 @@ export class TbMarkdownComponent implements OnChanges {
private render(markdown: string) {
const compiled = this.markdownService.parse(markdown, { decodeHtml: false });
let template = this.sanitizeCurlyBraces(compiled);
let markdownClass = 'tb-markdown-view';
if (this.markdownClass) {
markdownClass += ` ${this.markdownClass}`;
}
template = `<div [ngStyle]="style" class="${markdownClass}">${template}</div>`;
let template = `<div [ngStyle]="style" class="${markdownClass}">${compiled}</div>`;
if (this.containerClass) {
template = `<div class="${this.containerClass}" style="width: 100%; height: 100%;">${template}</div>`;
}
const element: HTMLDivElement = this.renderer.createElement('div');
element.innerHTML = template;
this.handlePlugins(element);
this.markdownService.highlight(element);
const preElements = element.querySelectorAll('pre');
const matches = Array.from(template.matchAll(/<pre[\S\s]+?(?=<\/pre>)<\/pre>/g));
for (let i = 0; i < preElements.length; i++) {
const preHtml = preElements.item(i).outerHTML.replace('ngnonbindable=""', 'ngNonBindable');
template = template.replace(matches[i][0], preHtml);
}
template = this.sanitizeCurlyBraces(template);
this.markdownContainer.clear();
const parent = this;
let readyObservable: Observable<void>;
@ -113,8 +129,14 @@ export class TbMarkdownComponent implements OnChanges {
if (this.additionalCompileModules) {
compileModules = compileModules.concat(this.additionalCompileModules);
}
let styles: string[] = deepClone(TbMarkdownComponent['ɵcmp'].styles);
styles[0] = styles[0].replace(/\[_nghost\-%COMP%\]/g, '').replace(/\[_ngcontent\-%COMP%\]/g, '');
let styles: string[] = [];
if (this.applyDefaultMarkdownStyle) {
if (!defaultMarkdownStyle) {
defaultMarkdownStyle = deepClone(TbMarkdownComponent['ɵcmp'].styles)[0].replace(/\[_nghost\-%COMP%\]/g, '')
.replace(/\[_ngcontent\-%COMP%\]/g, '');
}
styles.push(defaultMarkdownStyle);
}
if (this.additionalStyles) {
styles = styles.concat(this.additionalStyles);
}
@ -139,20 +161,18 @@ export class TbMarkdownComponent implements OnChanges {
}
}
this.tbMarkdownInstanceComponentRef.instance.style = this.style;
this.handlePlugins(this.tbMarkdownInstanceComponentRef.location.nativeElement);
this.markdownService.highlight(this.tbMarkdownInstanceComponentRef.location.nativeElement);
readyObservable = this.handleImages(this.tbMarkdownInstanceComponentRef.location.nativeElement);
this.cd.detectChanges();
this.error = null;
} catch (error) {
readyObservable = this.handleError(compiled, error);
readyObservable = this.handleError(template, error, styles);
}
readyObservable.subscribe(() => {
this.ready.emit();
});
},
(error) => {
readyObservable = this.handleError(compiled, error);
readyObservable = this.handleError(template, error, styles);
this.cd.detectChanges();
readyObservable.subscribe(() => {
this.ready.emit();
@ -160,14 +180,24 @@ export class TbMarkdownComponent implements OnChanges {
});
}
private handleError(template: string, error): Observable<void> {
private handleError(template: string, error, styles?: string[]): Observable<void> {
this.error = (error ? error + '' : 'Failed to render markdown!').replace(/\n/g, '<br>');
this.markdownContainer.clear();
if (this.fallbackToPlainMarkdownValue) {
const element = this.fallbackElement.nativeElement;
let styleElement;
if (styles?.length) {
const markdownClass = 'tb-markdown-view-' + guid();
let innerStyle = styles.join('\n');
innerStyle = innerStyle.replace(/\.tb-markdown-view/g, '.' + markdownClass);
template = template.replace(/tb-markdown-view/g, markdownClass);
styleElement = this.renderer.createElement('style');
styleElement.innerHTML = innerStyle;
}
element.innerHTML = template;
this.handlePlugins(element);
this.markdownService.highlight(element);
if (styleElement) {
this.renderer.appendChild(element, styleElement);
}
return this.handleImages(element);
} else {
return of(null);

1
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -4678,6 +4678,7 @@
"use-markdown-text-function": "Use markdown/HTML value function",
"markdown-text-function": "Markdown/HTML value function",
"markdown-text-pattern": "Markdown/HTML pattern (markdown or HTML with variables, for ex. '${entityName} or ${keyName} - some text.')",
"apply-default-markdown-style": "Apply default markdown style",
"markdown-css": "Markdown/HTML CSS"
},
"simple-card": {

49
ui-ngx/src/styles.scss

@ -424,55 +424,6 @@ mat-label {
}
}
.tb-markdown-view {
pre[class*="language-"] {
.token.atrule, .token.attr-value, .token.keyword {
color: #2a7dec;
}
.token.selector, .token.attr-name, .token.string, .token.char, .token.builtin, .token.inserted {
color: #eb5757;
}
.token.punctuation {
color: #212529;
}
&.line-numbers {
padding-left: 66px;
& > code {
span.line-numbers-rows {
top: -12px;
bottom: -12px;
left: -66px;
width: 50px;
border: none;
padding: 8px 12px 8px 18px;
text-align: right;
background: #f9fbff;
& > span:before {
color: rgba(33, 37, 41, .6);
padding-right: 0;
}
}
}
&.no-line-numbers {
padding-left: 16px;
& > code {
span.line-numbers-rows {
display: none;
}
}
}
}
}
}
// Tooltipster
.tooltipster-sidetip.tooltipster-tb {

Loading…
Cancel
Save