From f32653d17453a5040ad1381a66037597f0f19eee Mon Sep 17 00:00:00 2001 From: knyL Date: Wed, 13 Jan 2021 18:16:34 +0700 Subject: [PATCH 1/3] researching repo --- package.json | 1 + .../src/app/app.component.html | 3 +- .../src/app/app.component.ts | 104 +++++---- .../src/lib/angular-editor.component.ts | 217 ++++++++++-------- .../src/lib/angular-editor.service.ts | 110 +++++---- 5 files changed, 247 insertions(+), 188 deletions(-) diff --git a/package.json b/package.json index 31167fe99..b99d0feba 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "@angular/platform-browser-dynamic": "^10.2.0", "@angular/router": "^10.2.0", "font-awesome": "^4.7.0", + "insert-text-at-cursor": "^0.3.0", "rxjs": "~6.5.4", "tslib": "^2.0.0", "zone.js": "~0.10.2" diff --git a/projects/angular-editor-app/src/app/app.component.html b/projects/angular-editor-app/src/app/app.component.html index 407596cc0..ea2ec2662 100644 --- a/projects/angular-editor-app/src/app/app.component.html +++ b/projects/angular-editor-app/src/app/app.component.html @@ -5,7 +5,7 @@

Angular Editor



-

HTML Output: {{ htmlContent1 }} @@ -19,5 +19,6 @@

Angular Editor

Form Status: {{ form.status }}

+ Insert at cursor diff --git a/projects/angular-editor-app/src/app/app.component.ts b/projects/angular-editor-app/src/app/app.component.ts index f42001d55..fc5a3b993 100644 --- a/projects/angular-editor-app/src/app/app.component.ts +++ b/projects/angular-editor-app/src/app/app.component.ts @@ -1,102 +1,112 @@ -import {Component, OnInit} from '@angular/core'; -import {AngularEditorConfig} from 'angular-editor'; -import {FormBuilder, FormGroup, Validators} from '@angular/forms'; - +import { Component, OnInit, ViewChild } from "@angular/core"; +import { AngularEditorConfig } from "angular-editor"; +import { FormBuilder, FormGroup, Validators } from "@angular/forms"; +import { AngularEditorComponent } from "projects/angular-editor/src/public-api"; +import insertTextAtCursor from "insert-text-at-cursor"; @Component({ - selector: 'app-root', - templateUrl: './app.component.html', - styleUrls: ['./app.component.scss'] + selector: "app-root", + templateUrl: "./app.component.html", + styleUrls: ["./app.component.scss"], }) export class AppComponent implements OnInit { - title = 'app'; + title = "app"; + @ViewChild("editorRef") editorRef: AngularEditorComponent; form: FormGroup; - htmlContent1 = ''; - htmlContent2 = ''; + htmlContent1 = ""; + htmlContent2 = ""; config1: AngularEditorConfig = { editable: true, spellcheck: true, - minHeight: '5rem', - maxHeight: '15rem', - placeholder: 'Enter text here...', - translate: 'no', + minHeight: "5rem", + maxHeight: "15rem", + placeholder: "Enter text here...", + translate: "no", sanitize: false, // toolbarPosition: 'top', outline: true, - defaultFontName: 'Comic Sans MS', - defaultFontSize: '5', + defaultFontName: "Comic Sans MS", + defaultFontSize: "5", // showToolbar: false, - defaultParagraphSeparator: 'p', + defaultParagraphSeparator: "p", customClasses: [ { - name: 'quote', - class: 'quote', + name: "quote", + class: "quote", }, { - name: 'redText', - class: 'redText' + name: "redText", + class: "redText", }, { - name: 'titleText', - class: 'titleText', - tag: 'h1', + name: "titleText", + class: "titleText", + tag: "h1", }, ], - toolbarHiddenButtons: [ - ['bold', 'italic'], - ['fontSize'] - ] + toolbarHiddenButtons: [["bold", "italic"], ["fontSize"]], }; config2: AngularEditorConfig = { editable: true, spellcheck: true, - minHeight: '5rem', - maxHeight: '15rem', - placeholder: 'Enter text here...', - translate: 'no', + minHeight: "5rem", + maxHeight: "15rem", + placeholder: "Enter text here...", + translate: "no", sanitize: true, - toolbarPosition: 'bottom', - defaultFontName: 'Comic Sans MS', - defaultFontSize: '5', - defaultParagraphSeparator: 'p', + toolbarPosition: "bottom", + defaultFontName: "Comic Sans MS", + defaultFontSize: "5", + defaultParagraphSeparator: "p", customClasses: [ { - name: 'quote', - class: 'quote', + name: "quote", + class: "quote", }, { - name: 'redText', - class: 'redText' + name: "redText", + class: "redText", }, { - name: 'titleText', - class: 'titleText', - tag: 'h1', + name: "titleText", + class: "titleText", + tag: "h1", }, - ] + ], }; constructor(private formBuilder: FormBuilder) {} ngOnInit() { this.form = this.formBuilder.group({ - signature: ['', Validators.required] + signature: ["", Validators.required], }); console.log(this.htmlContent1); } onChange(event) { - console.log('changed'); + console.log("changed"); } onBlur(event) { - console.log('blur ' + event); + console.log("blur " + event); } onChange2(event) { console.warn(this.form.value); } + insertAtCursor() { + console.log(`insertAtCursors`); + this.editorRef.focus(); + setTimeout(()=> this.editorRef.insert("dsfd"),2000); + //this.editorRef.insert("dsfd"); + // + } + onTextAreaMouseOut(event) { + console.log(`onTextAreaMouseOut`); + console.log(event); + } } diff --git a/projects/angular-editor/src/lib/angular-editor.component.ts b/projects/angular-editor/src/lib/angular-editor.component.ts index 2d4132615..8d937e473 100644 --- a/projects/angular-editor/src/lib/angular-editor.component.ts +++ b/projects/angular-editor/src/lib/angular-editor.component.ts @@ -15,31 +15,31 @@ import { Output, Renderer2, SecurityContext, - ViewChild -} from '@angular/core'; -import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms'; -import {AngularEditorConfig, angularEditorConfig} from './config'; -import {AngularEditorToolbarComponent} from './angular-editor-toolbar.component'; -import {AngularEditorService} from './angular-editor.service'; -import {DOCUMENT} from '@angular/common'; -import {DomSanitizer} from '@angular/platform-browser'; -import {isDefined} from './utils'; + ViewChild, +} from "@angular/core"; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from "@angular/forms"; +import { AngularEditorConfig, angularEditorConfig } from "./config"; +import { AngularEditorToolbarComponent } from "./angular-editor-toolbar.component"; +import { AngularEditorService } from "./angular-editor.service"; +import { DOCUMENT } from "@angular/common"; +import { DomSanitizer } from "@angular/platform-browser"; +import { isDefined } from "./utils"; @Component({ - selector: 'angular-editor', - templateUrl: './angular-editor.component.html', - styleUrls: ['./angular-editor.component.scss'], + selector: "angular-editor", + templateUrl: "./angular-editor.component.html", + styleUrls: ["./angular-editor.component.scss"], providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => AngularEditorComponent), - multi: true + multi: true, }, - AngularEditorService - ] + AngularEditorService, + ], }) -export class AngularEditorComponent implements OnInit, ControlValueAccessor, AfterViewInit, OnDestroy { - +export class AngularEditorComponent + implements OnInit, ControlValueAccessor, AfterViewInit, OnDestroy { private onChange: (value: string) => void; private onTouched: () => void; @@ -53,30 +53,32 @@ export class AngularEditorComponent implements OnInit, ControlValueAccessor, Aft focusInstance: any; blurInstance: any; - @Input() id = ''; + @Input() id = ""; @Input() config: AngularEditorConfig = angularEditorConfig; - @Input() placeholder = ''; + @Input() placeholder = ""; @Input() tabIndex: number | null; @Output() html; - @ViewChild('editor', {static: true}) textArea: ElementRef; - @ViewChild('editorWrapper', {static: true}) editorWrapper: ElementRef; - @ViewChild('editorToolbar') editorToolbar: AngularEditorToolbarComponent; + @ViewChild("editor", { static: true }) textArea: ElementRef; + @ViewChild("editorWrapper", { static: true }) editorWrapper: ElementRef; + @ViewChild("editorToolbar") editorToolbar: AngularEditorToolbarComponent; @Output() viewMode = new EventEmitter(); /** emits `blur` event when focused out from the textarea */ - // tslint:disable-next-line:no-output-native no-output-rename - @Output('blur') blurEvent: EventEmitter = new EventEmitter(); + // tslint:disable-next-line:no-output-native no-output-rename + @Output("blur") + blurEvent: EventEmitter = new EventEmitter(); /** emits `focus` event when focused in to the textarea */ - // tslint:disable-next-line:no-output-rename no-output-native - @Output('focus') focusEvent: EventEmitter = new EventEmitter(); + // tslint:disable-next-line:no-output-rename no-output-native + @Output("focus") + focusEvent: EventEmitter = new EventEmitter(); - @HostBinding('attr.tabindex') tabindex = -1; + @HostBinding("attr.tabindex") tabindex = -1; - @HostListener('focus') + @HostListener("focus") onFocus() { this.focus(); } @@ -87,15 +89,18 @@ export class AngularEditorComponent implements OnInit, ControlValueAccessor, Aft @Inject(DOCUMENT) private doc: any, private sanitizer: DomSanitizer, private cdRef: ChangeDetectorRef, - @Attribute('tabindex') defaultTabIndex: string, - @Attribute('autofocus') private autoFocus: any + @Attribute("tabindex") defaultTabIndex: string, + @Attribute("autofocus") private autoFocus: any ) { const parsedTabIndex = Number(defaultTabIndex); - this.tabIndex = (parsedTabIndex || parsedTabIndex === 0) ? parsedTabIndex : null; + this.tabIndex = + parsedTabIndex || parsedTabIndex === 0 ? parsedTabIndex : null; } ngOnInit() { - this.config.toolbarPosition = this.config.toolbarPosition ? this.config.toolbarPosition : angularEditorConfig.toolbarPosition; + this.config.toolbarPosition = this.config.toolbarPosition + ? this.config.toolbarPosition + : angularEditorConfig.toolbarPosition; } ngAfterViewInit() { @@ -110,17 +115,17 @@ export class AngularEditorComponent implements OnInit, ControlValueAccessor, Aft */ executeCommand(command: string) { this.focus(); - if (command === 'focus') { + if (command === "focus") { return; } - if (command === 'toggleEditorMode') { + if (command === "toggleEditorMode") { this.toggleEditorMode(this.modeVisual); - } else if (command !== '') { - if (command === 'clear') { + } else if (command !== "") { + if (command === "clear") { this.editorService.removeSelectedElements(this.getCustomTags()); this.onContentChange(this.textArea.nativeElement); - } else if (command === 'default') { - this.editorService.removeSelectedElements('h1,h2,h3,h4,h5,h6,p,pre'); + } else if (command === "default") { + this.editorService.removeSelectedElements("h1,h2,h3,h4,h5,h6,p,pre"); this.onContentChange(this.textArea.nativeElement); } else { this.editorService.executeCommand(command); @@ -161,15 +166,20 @@ export class AngularEditorComponent implements OnInit, ControlValueAccessor, Aft /** * save selection if focussed out */ - this.editorService.executeInNextQueueIteration(this.editorService.saveSelection); + this.editorService.executeInNextQueueIteration( + this.editorService.saveSelection + ); - if (typeof this.onTouched === 'function') { + if (typeof this.onTouched === "function") { this.onTouched(); } if (event.relatedTarget !== null) { const parent = (event.relatedTarget as HTMLElement).parentElement; - if (!parent.classList.contains('angular-editor-toolbar-set') && !parent.classList.contains('ae-picker')) { + if ( + !parent.classList.contains("angular-editor-toolbar-set") && + !parent.classList.contains("ae-picker") + ) { this.blurEvent.emit(event); this.focused = false; } @@ -183,7 +193,7 @@ export class AngularEditorComponent implements OnInit, ControlValueAccessor, Aft if (this.modeVisual) { this.textArea.nativeElement.focus(); } else { - const sourceText = this.doc.getElementById('sourceText' + this.id); + const sourceText = this.doc.getElementById("sourceText" + this.id); sourceText.focus(); this.focused = true; } @@ -194,19 +204,22 @@ export class AngularEditorComponent implements OnInit, ControlValueAccessor, Aft * @param element html element from contenteditable */ onContentChange(element: HTMLElement): void { - let html = ''; + let html = ""; if (this.modeVisual) { html = element.innerHTML; } else { html = element.innerText; } - if ((!html || html === '
')) { - html = ''; + if (!html || html === "
") { + html = ""; } - if (typeof this.onChange === 'function') { - this.onChange(this.config.sanitize || this.config.sanitize === undefined ? - this.sanitizer.sanitize(SecurityContext.HTML, html) : html); - if ((!html) !== this.showPlaceholder) { + if (typeof this.onChange === "function") { + this.onChange( + this.config.sanitize || this.config.sanitize === undefined + ? this.sanitizer.sanitize(SecurityContext.HTML, html) + : html + ); + if (!html !== this.showPlaceholder) { this.togglePlaceholder(this.showPlaceholder); } } @@ -220,7 +233,7 @@ export class AngularEditorComponent implements OnInit, ControlValueAccessor, Aft * @param fn a function */ registerOnChange(fn: any): void { - this.onChange = e => (e === '
' ? fn('') : fn(e)) ; + this.onChange = (e) => (e === "
" ? fn("") : fn(e)); } /** @@ -239,12 +252,11 @@ export class AngularEditorComponent implements OnInit, ControlValueAccessor, Aft * @param value value to be executed when there is a change in contenteditable */ writeValue(value: any): void { - - if ((!value || value === '
' || value === '') !== this.showPlaceholder) { + if ((!value || value === "
" || value === "") !== this.showPlaceholder) { this.togglePlaceholder(this.showPlaceholder); } - if (value === undefined || value === '' || value === '
') { + if (value === undefined || value === "" || value === "
") { value = null; } @@ -257,8 +269,12 @@ export class AngularEditorComponent implements OnInit, ControlValueAccessor, Aft * @param value html string from the editor */ refreshView(value: string): void { - const normalizedValue = value === null ? '' : value; - this.r.setProperty(this.textArea.nativeElement, 'innerHTML', normalizedValue); + const normalizedValue = value === null ? "" : value; + this.r.setProperty( + this.textArea.nativeElement, + "innerHTML", + normalizedValue + ); return; } @@ -270,11 +286,10 @@ export class AngularEditorComponent implements OnInit, ControlValueAccessor, Aft */ togglePlaceholder(value: boolean): void { if (!value) { - this.r.addClass(this.editorWrapper.nativeElement, 'show-placeholder'); + this.r.addClass(this.editorWrapper.nativeElement, "show-placeholder"); this.showPlaceholder = true; - } else { - this.r.removeClass(this.editorWrapper.nativeElement, 'show-placeholder'); + this.r.removeClass(this.editorWrapper.nativeElement, "show-placeholder"); this.showPlaceholder = false; } } @@ -286,8 +301,8 @@ export class AngularEditorComponent implements OnInit, ControlValueAccessor, Aft */ setDisabledState(isDisabled: boolean): void { const div = this.textArea.nativeElement; - const action = isDisabled ? 'addClass' : 'removeClass'; - this.r[action](div, 'disabled'); + const action = isDisabled ? "addClass" : "removeClass"; + this.r[action](div, "disabled"); this.disabled = isDisabled; } @@ -302,43 +317,51 @@ export class AngularEditorComponent implements OnInit, ControlValueAccessor, Aft if (bToSource) { oContent = this.r.createText(editableElement.innerHTML); - this.r.setProperty(editableElement, 'innerHTML', ''); - this.r.setProperty(editableElement, 'contentEditable', false); - - const oPre = this.r.createElement('pre'); - this.r.setStyle(oPre, 'margin', '0'); - this.r.setStyle(oPre, 'outline', 'none'); - - const oCode = this.r.createElement('code'); - this.r.setProperty(oCode, 'id', 'sourceText' + this.id); - this.r.setStyle(oCode, 'display', 'block'); - this.r.setStyle(oCode, 'white-space', 'pre-wrap'); - this.r.setStyle(oCode, 'word-break', 'keep-all'); - this.r.setStyle(oCode, 'outline', 'none'); - this.r.setStyle(oCode, 'margin', '0'); - this.r.setStyle(oCode, 'background-color', '#fff5b9'); - this.r.setProperty(oCode, 'contentEditable', true); + this.r.setProperty(editableElement, "innerHTML", ""); + this.r.setProperty(editableElement, "contentEditable", false); + + const oPre = this.r.createElement("pre"); + this.r.setStyle(oPre, "margin", "0"); + this.r.setStyle(oPre, "outline", "none"); + + const oCode = this.r.createElement("code"); + this.r.setProperty(oCode, "id", "sourceText" + this.id); + this.r.setStyle(oCode, "display", "block"); + this.r.setStyle(oCode, "white-space", "pre-wrap"); + this.r.setStyle(oCode, "word-break", "keep-all"); + this.r.setStyle(oCode, "outline", "none"); + this.r.setStyle(oCode, "margin", "0"); + this.r.setStyle(oCode, "background-color", "#fff5b9"); + this.r.setProperty(oCode, "contentEditable", true); this.r.appendChild(oCode, oContent); - this.focusInstance = this.r.listen(oCode, 'focus', (event) => this.onTextAreaFocus(event)); - this.blurInstance = this.r.listen(oCode, 'blur', (event) => this.onTextAreaBlur(event)); + this.focusInstance = this.r.listen(oCode, "focus", (event) => + this.onTextAreaFocus(event) + ); + this.blurInstance = this.r.listen(oCode, "blur", (event) => + this.onTextAreaBlur(event) + ); this.r.appendChild(oPre, oCode); this.r.appendChild(editableElement, oPre); // ToDo move to service - this.doc.execCommand('defaultParagraphSeparator', false, 'div'); + this.doc.execCommand("defaultParagraphSeparator", false, "div"); this.modeVisual = false; this.viewMode.emit(false); oCode.focus(); } else { if (this.doc.querySelectorAll) { - this.r.setProperty(editableElement, 'innerHTML', editableElement.innerText); + this.r.setProperty( + editableElement, + "innerHTML", + editableElement.innerText + ); } else { oContent = this.doc.createRange(); oContent.selectNodeContents(editableElement.firstChild); - this.r.setProperty(editableElement, 'innerHTML', oContent.toString()); + this.r.setProperty(editableElement, "innerHTML", oContent.toString()); } - this.r.setProperty(editableElement, 'contentEditable', true); + this.r.setProperty(editableElement, "contentEditable", true); this.modeVisual = true; this.viewMode.emit(true); this.onContentChange(editableElement); @@ -358,12 +381,14 @@ export class AngularEditorComponent implements OnInit, ControlValueAccessor, Aft let userSelection; if (this.doc.getSelection) { userSelection = this.doc.getSelection(); - this.editorService.executeInNextQueueIteration(this.editorService.saveSelection); + this.editorService.executeInNextQueueIteration( + this.editorService.saveSelection + ); } let a = userSelection.focusNode; const els = []; - while (a && a.id !== 'editor') { + while (a && a.id !== "editor") { els.unshift(a); a = a.parentNode; } @@ -374,7 +399,9 @@ export class AngularEditorComponent implements OnInit, ControlValueAccessor, Aft this.editorService.uploadUrl = this.config.uploadUrl; this.editorService.uploadWithCredentials = this.config.uploadWithCredentials; if (this.config.defaultParagraphSeparator) { - this.editorService.setDefaultParagraphSeparator(this.config.defaultParagraphSeparator); + this.editorService.setDefaultParagraphSeparator( + this.config.defaultParagraphSeparator + ); } if (this.config.defaultFontName) { this.editorService.setFontName(this.config.defaultFontName); @@ -385,22 +412,28 @@ export class AngularEditorComponent implements OnInit, ControlValueAccessor, Aft } getFonts() { - const fonts = this.config.fonts ? this.config.fonts : angularEditorConfig.fonts; - return fonts.map(x => { - return {label: x.name, value: x.name}; + const fonts = this.config.fonts + ? this.config.fonts + : angularEditorConfig.fonts; + return fonts.map((x) => { + return { label: x.name, value: x.name }; }); } getCustomTags() { - const tags = ['span']; - this.config.customClasses.forEach(x => { + const tags = ["span"]; + this.config.customClasses.forEach((x) => { if (x.tag !== undefined) { if (!tags.includes(x.tag)) { tags.push(x.tag); } } }); - return tags.join(','); + return tags.join(","); + } + insert(value: string) { + + this.editorService.insertHtml("

helleo

"); } ngOnDestroy() { @@ -413,7 +446,7 @@ export class AngularEditorComponent implements OnInit, ControlValueAccessor, Aft } filterStyles(html: string): string { - html = html.replace('position: fixed;', ''); + html = html.replace("position: fixed;", ""); return html; } } diff --git a/projects/angular-editor/src/lib/angular-editor.service.ts b/projects/angular-editor/src/lib/angular-editor.service.ts index 12086dbc3..48373d58d 100644 --- a/projects/angular-editor/src/lib/angular-editor.service.ts +++ b/projects/angular-editor/src/lib/angular-editor.service.ts @@ -1,8 +1,8 @@ -import {Inject, Injectable} from '@angular/core'; -import {HttpClient, HttpEvent} from '@angular/common/http'; -import {Observable} from 'rxjs'; -import {DOCUMENT} from '@angular/common'; -import {CustomClass} from './config'; +import { Inject, Injectable } from "@angular/core"; +import { HttpClient, HttpEvent } from "@angular/common/http"; +import { Observable } from "rxjs"; +import { DOCUMENT } from "@angular/common"; +import { CustomClass } from "./config"; export interface UploadResponse { imageUrl: string; @@ -10,25 +10,21 @@ export interface UploadResponse { @Injectable() export class AngularEditorService { - savedSelection: Range | null; selectedText: string; uploadUrl: string; uploadWithCredentials: boolean; - constructor( - private http: HttpClient, - @Inject(DOCUMENT) private doc: any - ) { } + constructor(private http: HttpClient, @Inject(DOCUMENT) private doc: any) {} /** * Executed command from editor header buttons exclude toggleEditorMode * @param command string from triggerCommand */ executeCommand(command: string) { - const commands = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'pre']; + const commands = ["h1", "h2", "h3", "h4", "h5", "h6", "p", "pre"]; if (commands.includes(command)) { - this.doc.execCommand('formatBlock', false, command); + this.doc.execCommand("formatBlock", false, command); return; } this.doc.execCommand(command, false, null); @@ -39,10 +35,11 @@ export class AngularEditorService { * @param url string from UI prompt */ createLink(url: string) { - if (!url.includes('http')) { - this.doc.execCommand('createlink', false, url); + if (!url.includes("http")) { + this.doc.execCommand("createlink", false, url); } else { - const newUrl = '' + this.selectedText + ''; + const newUrl = + '' + this.selectedText + ""; this.insertHtml(newUrl); } } @@ -56,10 +53,10 @@ export class AngularEditorService { insertColor(color: string, where: string): void { const restored = this.restoreSelection(); if (restored) { - if (where === 'textColor') { - this.doc.execCommand('foreColor', false, color); + if (where === "textColor") { + this.doc.execCommand("foreColor", false, color); } else { - this.doc.execCommand('hiliteColor', false, color); + this.doc.execCommand("hiliteColor", false, color); } } } @@ -69,7 +66,7 @@ export class AngularEditorService { * @param fontName string */ setFontName(fontName: string) { - this.doc.execCommand('fontName', false, fontName); + this.doc.execCommand("fontName", false, fontName); } /** @@ -77,7 +74,7 @@ export class AngularEditorService { * @param fontSize string */ setFontSize(fontSize: string) { - this.doc.execCommand('fontSize', false, fontSize); + this.doc.execCommand("fontSize", false, fontSize); } /** @@ -85,11 +82,10 @@ export class AngularEditorService { * @param html HTML string */ insertHtml(html: string): void { - - const isHTMLInserted = this.doc.execCommand('insertHTML', false, html); + const isHTMLInserted = this.doc.execCommand("insertHTML", false, html); if (!isHTMLInserted) { - throw new Error('Unable to perform the operation'); + throw new Error("Unable to perform the operation"); } } @@ -97,6 +93,7 @@ export class AngularEditorService { * save selection when the editor is focussed out */ public saveSelection = (): void => { + console.log(this.doc.getSelection()); if (this.doc.getSelection) { const sel = this.doc.getSelection(); if (sel.getRangeAt && sel.rangeCount) { @@ -108,7 +105,7 @@ export class AngularEditorService { } else { this.savedSelection = null; } - } + }; /** * restore selection when the editor is focused in @@ -134,17 +131,19 @@ export class AngularEditorService { /** * setTimeout used for execute 'saveSelection' method in next event loop iteration */ - public executeInNextQueueIteration(callbackFn: (...args: any[]) => any, timeout = 1e2): void { + public executeInNextQueueIteration( + callbackFn: (...args: any[]) => any, + timeout = 1e2 + ): void { setTimeout(callbackFn, timeout); } /** check any selection is made or not */ private checkSelection(): any { - const selectedText = this.savedSelection.toString(); if (selectedText.length === 0) { - throw new Error('No Selection Made'); + throw new Error("No Selection Made"); } return true; } @@ -154,14 +153,13 @@ export class AngularEditorService { * @param file The file */ uploadImage(file: File): Observable> { - const uploadData: FormData = new FormData(); - uploadData.append('file', file, file.name); + uploadData.append("file", file, file.name); return this.http.post(this.uploadUrl, uploadData, { reportProgress: true, - observe: 'events', + observe: "events", withCredentials: this.uploadWithCredentials, }); } @@ -171,33 +169,42 @@ export class AngularEditorService { * @param imageUrl The imageUrl. */ insertImage(imageUrl: string) { - this.doc.execCommand('insertImage', false, imageUrl); + this.doc.execCommand("insertImage", false, imageUrl); } setDefaultParagraphSeparator(separator: string) { - this.doc.execCommand('defaultParagraphSeparator', false, separator); + this.doc.execCommand("defaultParagraphSeparator", false, separator); } createCustomClass(customClass: CustomClass) { let newTag = this.selectedText; if (customClass) { - const tagName = customClass.tag ? customClass.tag : 'span'; - newTag = '<' + tagName + ' class="' + customClass.class + '">' + this.selectedText + ''; + const tagName = customClass.tag ? customClass.tag : "span"; + newTag = + "<" + + tagName + + ' class="' + + customClass.class + + '">' + + this.selectedText + + ""; } this.insertHtml(newTag); } insertVideo(videoUrl: string) { - if (videoUrl.match('www.youtube.com')) { + if (videoUrl.match("www.youtube.com")) { this.insertYouTubeVideoTag(videoUrl); } - if (videoUrl.match('vimeo.com')) { + if (videoUrl.match("vimeo.com")) { this.insertVimeoVideoTag(videoUrl); } } private insertYouTubeVideoTag(videoUrl: string): void { - const id = videoUrl.split('v=')[1]; + const id = videoUrl.split("v=")[1]; const imageUrl = `https://img.youtube.com/vi/${id}/0.jpg`; const thumbnail = `
@@ -211,16 +218,18 @@ export class AngularEditorService { } private insertVimeoVideoTag(videoUrl: string): void { - const sub = this.http.get(`https://vimeo.com/api/oembed.json?url=${videoUrl}`).subscribe(data => { - const imageUrl = data.thumbnail_url_with_play_button; - const thumbnail = `
+ const sub = this.http + .get(`https://vimeo.com/api/oembed.json?url=${videoUrl}`) + .subscribe((data) => { + const imageUrl = data.thumbnail_url_with_play_button; + const thumbnail = ``; - this.insertHtml(thumbnail); - sub.unsubscribe(); - }); + this.insertHtml(thumbnail); + sub.unsubscribe(); + }); } nextNode(node) { @@ -248,7 +257,7 @@ export class AngularEditorService { } else { // Iterate nodes until we hit the end container while (node && node !== endNode) { - rangeNodes.push( node = this.nextNode(node) ); + rangeNodes.push((node = this.nextNode(node))); } // Add partially selected nodes at the start of the range @@ -276,7 +285,10 @@ export class AngularEditorService { if (this.doc.getSelection) { const sel = this.doc.getSelection(); for (let i = 0, len = sel.rangeCount; i < len; ++i) { - nodes.push.apply(nodes, this.getRangeSelectedNodes(sel.getRangeAt(i), true)); + nodes.push.apply( + nodes, + this.getRangeSelectedNodes(sel.getRangeAt(i), true) + ); } } return nodes; @@ -291,10 +303,12 @@ export class AngularEditorService { } removeSelectedElements(tagNames) { - const tagNamesArray = tagNames.toLowerCase().split(','); + const tagNamesArray = tagNames.toLowerCase().split(","); this.getSelectedNodes().forEach((node) => { - if (node.nodeType === 1 && - tagNamesArray.indexOf(node.tagName.toLowerCase()) > -1) { + if ( + node.nodeType === 1 && + tagNamesArray.indexOf(node.tagName.toLowerCase()) > -1 + ) { // Remove the node and replace it with its children this.replaceWithOwnChildren(node); } From d5407ad7dbbf0a10785b260c3d2870c98ff68044 Mon Sep 17 00:00:00 2001 From: knyL Date: Wed, 13 Jan 2021 22:35:43 +0700 Subject: [PATCH 2/3] testing insert() --- package.json | 1 - .../src/app/app.component.ts | 9 +++---- .../src/lib/angular-editor.component.ts | 24 +++++++++++++++---- .../src/lib/angular-editor.service.ts | 1 - 4 files changed, 25 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index b99d0feba..31167fe99 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,6 @@ "@angular/platform-browser-dynamic": "^10.2.0", "@angular/router": "^10.2.0", "font-awesome": "^4.7.0", - "insert-text-at-cursor": "^0.3.0", "rxjs": "~6.5.4", "tslib": "^2.0.0", "zone.js": "~0.10.2" diff --git a/projects/angular-editor-app/src/app/app.component.ts b/projects/angular-editor-app/src/app/app.component.ts index fc5a3b993..da65f853b 100644 --- a/projects/angular-editor-app/src/app/app.component.ts +++ b/projects/angular-editor-app/src/app/app.component.ts @@ -1,8 +1,7 @@ import { Component, OnInit, ViewChild } from "@angular/core"; -import { AngularEditorConfig } from "angular-editor"; import { FormBuilder, FormGroup, Validators } from "@angular/forms"; +import { AngularEditorConfig } from "angular-editor"; import { AngularEditorComponent } from "projects/angular-editor/src/public-api"; -import insertTextAtCursor from "insert-text-at-cursor"; @Component({ selector: "app-root", templateUrl: "./app.component.html", @@ -98,10 +97,12 @@ export class AppComponent implements OnInit { onChange2(event) { console.warn(this.form.value); } + cnt = 0; insertAtCursor() { console.log(`insertAtCursors`); - this.editorRef.focus(); - setTimeout(()=> this.editorRef.insert("dsfd"),2000); + //this.editorRef.focus(); + this.editorRef.insert(`-${this.cnt}-`); + this.cnt++; //this.editorRef.insert("dsfd"); // } diff --git a/projects/angular-editor/src/lib/angular-editor.component.ts b/projects/angular-editor/src/lib/angular-editor.component.ts index 8d937e473..fd3e16903 100644 --- a/projects/angular-editor/src/lib/angular-editor.component.ts +++ b/projects/angular-editor/src/lib/angular-editor.component.ts @@ -157,6 +157,7 @@ export class AngularEditorComponent */ public onTextAreaMouseOut(event: MouseEvent): void { this.editorService.saveSelection(); + this.savedSelection = this.editorService.savedSelection; } /** @@ -394,7 +395,6 @@ export class AngularEditorComponent } this.editorToolbar.triggerBlocks(els); } - private configure() { this.editorService.uploadUrl = this.config.uploadUrl; this.editorService.uploadWithCredentials = this.config.uploadWithCredentials; @@ -432,10 +432,26 @@ export class AngularEditorComponent return tags.join(","); } insert(value: string) { - - this.editorService.insertHtml("

helleo

"); + this.focus(); + let selection = window.getSelection(); + //let range = document.createRange(); + //range.setEndAfter(this.selectiedNode); + //selection.addRange(range); + //selection.setPosition(this.textArea.nativeElement, this.selectiedIndex); + // this.selectiedIndex += value.length; + selection.removeAllRanges(); + selection.addRange(this.savedSelection); + this.editorService.insertHtml(value); + selection.setPosition(this.savedSelection.baseNode, 5); + // selection.removeAllRanges(); + // let range = document.createRange(); + // selection.setPosition( + // this.textArea.nativeElement, + // this.savedSelection.baseOffet + // ); + // this.savedSelection = selection.getRangeAt(0); } - + savedSelection; ngOnDestroy() { if (this.blurInstance) { this.blurInstance(); diff --git a/projects/angular-editor/src/lib/angular-editor.service.ts b/projects/angular-editor/src/lib/angular-editor.service.ts index 48373d58d..6b5cda812 100644 --- a/projects/angular-editor/src/lib/angular-editor.service.ts +++ b/projects/angular-editor/src/lib/angular-editor.service.ts @@ -83,7 +83,6 @@ export class AngularEditorService { */ insertHtml(html: string): void { const isHTMLInserted = this.doc.execCommand("insertHTML", false, html); - if (!isHTMLInserted) { throw new Error("Unable to perform the operation"); } From 6b8cfd2d598f484ba66427f82444e056ede14947 Mon Sep 17 00:00:00 2001 From: knyL Date: Thu, 14 Jan 2021 09:56:17 +0700 Subject: [PATCH 3/3] add inserttextAtCursor --- .../src/app/app.component.html | 2 +- .../src/app/app.component.ts | 8 ++---- .../src/lib/angular-editor.component.ts | 28 ++++++------------- .../src/lib/angular-editor.service.ts | 1 - 4 files changed, 11 insertions(+), 28 deletions(-) diff --git a/projects/angular-editor-app/src/app/app.component.html b/projects/angular-editor-app/src/app/app.component.html index ea2ec2662..1f315f218 100644 --- a/projects/angular-editor-app/src/app/app.component.html +++ b/projects/angular-editor-app/src/app/app.component.html @@ -19,6 +19,6 @@

Angular Editor

Form Status: {{ form.status }}

- Insert at cursor + Insert at cursor
diff --git a/projects/angular-editor-app/src/app/app.component.ts b/projects/angular-editor-app/src/app/app.component.ts index da65f853b..5582df6b0 100644 --- a/projects/angular-editor-app/src/app/app.component.ts +++ b/projects/angular-editor-app/src/app/app.component.ts @@ -98,13 +98,9 @@ export class AppComponent implements OnInit { console.warn(this.form.value); } cnt = 0; - insertAtCursor() { - console.log(`insertAtCursors`); - //this.editorRef.focus(); - this.editorRef.insert(`-${this.cnt}-`); + insertTextAtCursor() { + this.editorRef.insertTextAtCursor(`{${this.cnt}}`); this.cnt++; - //this.editorRef.insert("dsfd"); - // } onTextAreaMouseOut(event) { console.log(`onTextAreaMouseOut`); diff --git a/projects/angular-editor/src/lib/angular-editor.component.ts b/projects/angular-editor/src/lib/angular-editor.component.ts index fd3e16903..f3490b8e4 100644 --- a/projects/angular-editor/src/lib/angular-editor.component.ts +++ b/projects/angular-editor/src/lib/angular-editor.component.ts @@ -52,7 +52,7 @@ export class AngularEditorComponent focusInstance: any; blurInstance: any; - + currentCursorSelection: Range; @Input() id = ""; @Input() config: AngularEditorConfig = angularEditorConfig; @Input() placeholder = ""; @@ -157,7 +157,7 @@ export class AngularEditorComponent */ public onTextAreaMouseOut(event: MouseEvent): void { this.editorService.saveSelection(); - this.savedSelection = this.editorService.savedSelection; + this.currentCursorSelection = this.editorService.savedSelection; } /** @@ -431,27 +431,15 @@ export class AngularEditorComponent }); return tags.join(","); } - insert(value: string) { - this.focus(); + insertTextAtCursor(text: string) { let selection = window.getSelection(); - //let range = document.createRange(); - //range.setEndAfter(this.selectiedNode); - //selection.addRange(range); - //selection.setPosition(this.textArea.nativeElement, this.selectiedIndex); - // this.selectiedIndex += value.length; selection.removeAllRanges(); - selection.addRange(this.savedSelection); - this.editorService.insertHtml(value); - selection.setPosition(this.savedSelection.baseNode, 5); - // selection.removeAllRanges(); - // let range = document.createRange(); - // selection.setPosition( - // this.textArea.nativeElement, - // this.savedSelection.baseOffet - // ); - // this.savedSelection = selection.getRangeAt(0); + selection.addRange(this.currentCursorSelection); + this.editorService.insertHtml(text); + this.editorService.saveSelection(); + this.currentCursorSelection = this.editorService.savedSelection; } - savedSelection; + ngOnDestroy() { if (this.blurInstance) { this.blurInstance(); diff --git a/projects/angular-editor/src/lib/angular-editor.service.ts b/projects/angular-editor/src/lib/angular-editor.service.ts index 6b5cda812..088aba82f 100644 --- a/projects/angular-editor/src/lib/angular-editor.service.ts +++ b/projects/angular-editor/src/lib/angular-editor.service.ts @@ -92,7 +92,6 @@ export class AngularEditorService { * save selection when the editor is focussed out */ public saveSelection = (): void => { - console.log(this.doc.getSelection()); if (this.doc.getSelection) { const sel = this.doc.getSelection(); if (sel.getRangeAt && sel.rangeCount) {