From 0151ad36c1942ce572bcf03929a8c6852b4726ee Mon Sep 17 00:00:00 2001 From: Ruud Faessen Date: Tue, 9 Jun 2020 08:51:37 +0200 Subject: [PATCH] add markdown and rich text editor as dependencies --- .../forms/markdown-editor.component.ts | 215 +++++++++--------- .../components/forms/rich-editor.component.ts | 34 +-- frontend/package-lock.json | 34 +++ frontend/package.json | 3 + 4 files changed, 163 insertions(+), 123 deletions(-) diff --git a/frontend/app/shared/components/forms/markdown-editor.component.ts b/frontend/app/shared/components/forms/markdown-editor.component.ts index b8a3c0972..af4759f8c 100644 --- a/frontend/app/shared/components/forms/markdown-editor.component.ts +++ b/frontend/app/shared/components/forms/markdown-editor.component.ts @@ -7,10 +7,11 @@ import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, forwardRef, Renderer2, ViewChild } from '@angular/core'; import { NG_VALUE_ACCESSOR } from '@angular/forms'; -import { ApiUrlConfig, AssetDto, AssetUploaderState, DialogModel, ResourceLoaderService, StatefulControlComponent, Types, UploadCanceled } from '@app/shared/internal'; +import { ApiUrlConfig, AssetDto, AssetUploaderState, DialogModel, StatefulControlComponent, Types, UploadCanceled } from '@app/shared/internal'; import marked from 'marked'; -declare var SimpleMDE: any; +import SimpleMDE from 'simplemde'; +import 'simplemde/dist/simplemde.min.css'; export const SQX_MARKDOWN_EDITOR_CONTROL_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => MarkdownEditorComponent), multi: true @@ -49,8 +50,7 @@ export class MarkdownEditorComponent extends StatefulControlComponent { - this.simplemde = new SimpleMDE({ - previewRender: (text: string) => { - return marked(text, { pedantic: true }); + this.simplemde = new SimpleMDE({ + previewRender: (text: string) => { + return marked(text, { pedantic: true }); + }, + autoDownloadFontAwesome: true, + toolbar: [ + { + name: 'bold', + action: SimpleMDE.toggleBold, + className: 'fa fa-bold', + title: 'Bold' + }, { + name: 'italic', + action: SimpleMDE.toggleItalic, + className: 'fa fa-italic', + title: 'Italic' + }, { + name: 'heading', + action: SimpleMDE.toggleHeadingSmaller, + className: 'fa fa-header', + title: 'Heading' + }, { + name: 'quote', + action: SimpleMDE.toggleBlockquote, + className: 'fa fa-quote-left', + title: 'Quote' + }, { + name: 'unordered-list', + action: SimpleMDE.toggleUnorderedList, + className: 'fa fa-list-ul', + title: 'Generic List' + }, { + name: 'ordered-list', + action: SimpleMDE.toggleOrderedList, + className: 'fa fa-list-ol', + title: 'Numbered List' }, - autoDownloadFontAwesome: true, - toolbar: [ - { - name: 'bold', - action: SimpleMDE.toggleBold, - className: 'fa fa-bold', - title: 'Bold' - }, { - name: 'italic', - action: SimpleMDE.toggleItalic, - className: 'fa fa-italic', - title: 'Italic' - }, { - name: 'heading', - action: SimpleMDE.toggleHeadingSmaller, - className: 'fa fa-header', - title: 'Heading' - }, { - name: 'quote', - action: SimpleMDE.toggleBlockquote, - className: 'fa fa-quote-left', - title: 'Quote' - }, { - name: 'unordered-list', - action: SimpleMDE.toggleUnorderedList, - className: 'fa fa-list-ul', - title: 'Generic List' - }, { - name: 'ordered-list', - action: SimpleMDE.toggleOrderedList, - className: 'fa fa-list-ol', - title: 'Numbered List' - }, - '|', - { - name: 'link', - action: SimpleMDE.drawLink, - className: 'fa fa-link', - title: 'Create Link' - }, { - name: 'image', - action: SimpleMDE.drawImage, - className: 'fa fa-picture-o', - title: 'Insert Image' - }, - '|', - { - name: 'preview', - action: SimpleMDE.togglePreview, - className: 'fa fa-eye no-disable', - title: 'Toggle Preview' - }, { - name: 'fullscreen', - action: SimpleMDE.toggleFullScreen, - className: 'fa fa-arrows-alt no-disable no-mobile', - title: 'Toggle Fullscreen' - }, { - name: 'side-by-side', - action: SimpleMDE.toggleSideBySide, - className: 'fa fa-columns no-disable no-mobile', - title: 'Toggle Side by Side' - }, - '|', - { - name: 'guide', - action: 'https://simplemde.com/markdown-guide', - className: 'fa fa-question-circle', - title: 'Markdown Guide' - }, - '|', - { - name: 'assets', - action: this.showSelector, - className: 'icon-assets icon-bold', - title: 'Insert Assets' - } - ], - element: this.editor.nativeElement - }); + '|', + { + name: 'link', + action: SimpleMDE.drawLink, + className: 'fa fa-link', + title: 'Create Link' + }, { + name: 'image', + action: SimpleMDE.drawImage, + className: 'fa fa-picture-o', + title: 'Insert Image' + }, + '|', + { + name: 'preview', + action: SimpleMDE.togglePreview, + className: 'fa fa-eye no-disable', + title: 'Toggle Preview' + }, { + name: 'fullscreen', + action: SimpleMDE.toggleFullScreen, + className: 'fa fa-arrows-alt no-disable no-mobile', + title: 'Toggle Fullscreen' + }, { + name: 'side-by-side', + action: SimpleMDE.toggleSideBySide, + className: 'fa fa-columns no-disable no-mobile', + title: 'Toggle Side by Side' + }, + '|', + { + name: 'guide', + action: 'https://simplemde.com/markdown-guide', + className: 'fa fa-question-circle', + title: 'Markdown Guide' + }, + '|', + { + name: 'assets', + action: this.showSelector, + className: 'icon-assets icon-bold', + title: 'Insert Assets' + } + ], + element: this.editor.nativeElement + }); - this.simplemde.value(this.value || ''); - this.simplemde.codemirror.setOption('readOnly', this.isDisabled); + this.simplemde.value(this.value || ''); + this.simplemde.codemirror.setOption('readOnly', this.isDisabled); - this.simplemde.codemirror.on('change', () => { - const value = this.simplemde.value(); + this.simplemde.codemirror.on('change', () => { + const value = this.simplemde.value(); - if (this.value !== value) { - this.value = value; + if (this.value !== value) { + this.value = value; - this.callChange(value); - } - }); + this.callChange(value); + } + }); - this.simplemde.codemirror.on('refresh', () => { - const isFullscreen = this.simplemde.isFullscreenActive(); + this.simplemde.codemirror.on('refresh', () => { + const isFullscreen = this.simplemde.isFullscreenActive(); - let target = this.container.nativeElement; + let target = this.container.nativeElement; - if (isFullscreen) { - target = document.body; - } + if (isFullscreen) { + target = document.body; + } - this.renderer.appendChild(target, this.inner.nativeElement); + this.renderer.appendChild(target, this.inner.nativeElement); - this.next(s => ({ ...s, isFullscreen })); - }); + this.next(s => ({ ...s, isFullscreen })); + }); - this.simplemde.codemirror.on('blur', () => { - this.callTouched(); - }); + this.simplemde.codemirror.on('blur', () => { + this.callTouched(); }); } diff --git a/frontend/app/shared/components/forms/rich-editor.component.ts b/frontend/app/shared/components/forms/rich-editor.component.ts index 639ce88b9..34696ab53 100644 --- a/frontend/app/shared/components/forms/rich-editor.component.ts +++ b/frontend/app/shared/components/forms/rich-editor.component.ts @@ -9,9 +9,19 @@ import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, forwardRef, OnDestroy, Output, ViewChild } from '@angular/core'; import { NG_VALUE_ACCESSOR } from '@angular/forms'; -import { ApiUrlConfig, AssetDto, AssetUploaderState, DialogModel, ResourceLoaderService, StatefulControlComponent, Types, UploadCanceled } from '@app/shared/internal'; +import { ApiUrlConfig, AssetDto, AssetUploaderState, DialogModel, StatefulControlComponent, Types, UploadCanceled } from '@app/shared/internal'; -declare var tinymce: any; +import tinymce from 'tinymce'; +import 'tinymce/themes/silver'; + +import 'tinymce/plugins/advlist'; +import 'tinymce/plugins/code'; +import 'tinymce/plugins/image'; +import 'tinymce/plugins/link'; +import 'tinymce/plugins/lists'; +import 'tinymce/plugins/media'; +import 'tinymce/plugins/paste'; +import 'tinymce/skins/ui/oxide/skin.min.css'; export const SQX_RICH_EDITOR_CONTROL_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => RichEditorComponent), multi: true @@ -48,8 +58,7 @@ export class RichEditorComponent extends StatefulControlComponent { - const timer = setInterval(() => { - const target = this.editor.nativeElement; + const timer = setInterval(() => { + const target = this.editor.nativeElement; - if (document.body.contains(target)) { - tinymce.init(this.getEditorOptions(target)); + if (document.body.contains(target)) { + tinymce.init(this.getEditorOptions(target)); - clearInterval(timer); - } - }, 10); - }); + clearInterval(timer); + } + }, 10); } public reset() { @@ -96,7 +103,6 @@ export class RichEditorComponent extends StatefulControlComponent void, failure: (message: string) => void) => { const file = new File([blob.blob()], blob.filename(), { lastModified: new Date().getTime() }); diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 662ceabc1..3efaecd25 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -573,6 +573,12 @@ "@types/react": "*" } }, + "@types/simplemde": { + "version": "1.11.7", + "resolved": "https://registry.npmjs.org/@types/simplemde/-/simplemde-1.11.7.tgz", + "integrity": "sha512-b3yirBar1gqb9clgJJKpx+Or3txkOOkm5sffhRKaBUfh7+1D2Aimx2RKXGS1LK9S5KyNmug3lwpGJtaID9cOnA==", + "dev": true + }, "@types/sizzle": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.2.tgz", @@ -2300,6 +2306,14 @@ "graphql-language-service-parser": "^1.5.2" } }, + "codemirror-spell-checker": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/codemirror-spell-checker/-/codemirror-spell-checker-1.1.2.tgz", + "integrity": "sha1-HGYPkIlIPMtRE7m6nKGcP0mTNx4=", + "requires": { + "typo-js": "*" + } + }, "collection-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", @@ -11181,6 +11195,16 @@ } } }, + "simplemde": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/simplemde/-/simplemde-1.11.2.tgz", + "integrity": "sha1-ojo12XjSxA7wfewAjJLwcNjggOM=", + "requires": { + "codemirror": "*", + "codemirror-spell-checker": "*", + "marked": "*" + } + }, "slice-ansi": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", @@ -12293,6 +12317,11 @@ "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", "dev": true }, + "tinymce": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/tinymce/-/tinymce-5.2.0.tgz", + "integrity": "sha512-Q7KAu9sLB6TBhKFdb2LHPGy770zkSEjpN1VRqZ6pxNuVQ0mbGWgMocHDvM9XL9yJaOhFrJP6s9XM7zG2gapGpA==" + }, "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -12627,6 +12656,11 @@ "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==", "dev": true }, + "typo-js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/typo-js/-/typo-js-1.1.0.tgz", + "integrity": "sha512-W3kLbx+ML9PBl5Bzso/lTvVxk4BCveSNAtQeht59FEtxCdGThmn6wSHA4Xq3eQYAK24NHdisMM4JmsK0GFy/pg==" + }, "uc.micro": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", diff --git a/frontend/package.json b/frontend/package.json index bf2e3c98a..39aac8dda 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -45,7 +45,9 @@ "react": "16.10.2", "react-dom": "16.10.2", "rxjs": "6.5.4", + "simplemde": "^1.11.2", "slugify": "1.4.0", + "tinymce": "^5.2.0", "tslib": "1.11.1", "zone.js": "0.10.3" }, @@ -62,6 +64,7 @@ "@types/node": "13.7.4", "@types/react": "16.9.21", "@types/react-dom": "16.9.5", + "@types/simplemde": "^1.11.2", "@types/tinymce": "4.5.24", "browserslist": "4.9.1", "caniuse-lite": "1.0.30001055",