Skip to content

Commit

Permalink
feat: option to open url in new tab
Browse files Browse the repository at this point in the history
  • Loading branch information
sibiraj-s committed Feb 19, 2018
1 parent 62f4522 commit d4001c4
Show file tree
Hide file tree
Showing 11 changed files with 1,047 additions and 299 deletions.
1,096 changes: 831 additions & 265 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
},
"devDependencies": {
"@angular/animations": "^5.2.0",
"@angular/cli": "1.6.7",
"@angular/cli": "1.6.8",
"@angular/common": "^5.2.0",
"@angular/compiler": "^5.2.0",
"@angular/compiler-cli": "^5.2.0",
Expand All @@ -57,7 +57,7 @@
"@angular/platform-browser": "^5.2.0",
"@angular/platform-browser-dynamic": "^5.2.0",
"@angular/router": "^5.2.0",
"@compodoc/compodoc": "^1.0.5",
"@compodoc/compodoc": "^1.0.6",
"@types/jasmine": "~2.8.3",
"@types/jasminewd2": "~2.0.2",
"@types/node": "~6.0.60",
Expand All @@ -78,6 +78,7 @@
"karma-jasmine": "~1.1.0",
"karma-jasmine-html-reporter": "^0.2.2",
"ng-packagr": "^2.0.0",
"ngx-bootstrap": "^2.0.2",
"protractor": "~5.1.2",
"rxjs": "^5.5.6",
"ts-node": "~4.1.0",
Expand Down
68 changes: 51 additions & 17 deletions src/app/ngx-editor/common/services/command-executor.service.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { Injectable } from '@angular/core';
import * as Utils from '../utils/ngx-editor.utils';

@Injectable()
export class CommandExecutorService {

savedSelection: any = undefined;

/**
* executes command from the toolbar
*
Expand All @@ -25,10 +28,10 @@ export class CommandExecutorService {
return;
}

if (command === 'link') {
this.createLink();
return;
}
// if (command === 'link') {
// this.createLink();
// return;
// }

if (command === 'image') {
this.insertImage();
Expand All @@ -49,24 +52,55 @@ export class CommandExecutorService {
return;
}

private createLink(): void {
const selection = document.getSelection();
// private createLink(): void {
// const selection = document.getSelection();

if (selection.anchorNode.parentElement.tagName === 'A') {
const linkURL = prompt('Enter URL', selection.anchorNode.parentElement.getAttribute('href'));
if (linkURL) {
document.execCommand('createLink', false, linkURL);
}
} else {
if (selection['type'] === 'None') {
throw new Error('No selection made');
// if (selection.anchorNode.parentElement.tagName === 'A') {
// const linkURL = prompt('Enter URL', selection.anchorNode.parentElement.getAttribute('href'));
// if (linkURL) {
// document.execCommand('createLink', false, linkURL);
// }
// } else {
// if (selection['type'] === 'None') {
// throw new Error('No selection made');
// } else {
// const linkURL = prompt('Enter URL', 'http://');
// if (linkURL) {
// document.execCommand('createLink', false, linkURL);
// }
// }
// }
// return;
// }

createLink(params: any): void {

if (this.savedSelection) {
/**
* check whether the saved selection contains a range or plain selection
*/
if (params.urlNewTab) {
const newUrl = '<a href="' + params.urlLink + '" target="_blank">' + params.urlText + '</a>';

if (document.getSelection().type !== 'Range') {
const restored = Utils.restoreSelection(this.savedSelection);
if (restored) {
document.execCommand('insertHTML', false, newUrl);
}
document.execCommand('insertHTML', false, newUrl);
} else {
throw new Error('Only new links can be inserted. You cannot edit URL`s');
}
} else {
const linkURL = prompt('Enter URL', 'http://');
if (linkURL) {
document.execCommand('createLink', false, linkURL);
const restored = Utils.restoreSelection(this.savedSelection);
if (restored) {
document.execCommand('createLink', false, params.urlLink);
}
}
} else {
throw new Error('Range out of the editor');
}

return;
}

Expand Down
37 changes: 37 additions & 0 deletions src/app/ngx-editor/common/utils/ngx-editor.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,40 @@ export function canResize(resizer: string): any {
}
return false;
}

/**
* save selection when the editor is focussed out
*/
export function saveSelection(): any {
if (window.getSelection) {
const sel = window.getSelection();
if (sel.getRangeAt && sel.rangeCount) {
return sel.getRangeAt(0);
}
} else if (document.getSelection && document.createRange) {
return document.createRange();
}
return null;
}

/**
* restore selection when the editor is focussed in
*
* @param range saved selection when the editor is focussed out
*/
export function restoreSelection(range): boolean {
if (range) {
if (window.getSelection) {
const sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
return true;
} else if (document.getSelection && range.select) {
range.select();
return true;
}
} else {
return false;
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,8 @@
</button>
</div>
<div class="ngx-toolbar-set">
<button type="button" class="ngx-editor-button" *ngIf="canEnableToolbarOptions('link')" (click)="triggerCommand('link')"
title="Insert/Edit Link" [disabled]="!config['enableToolbar']">
<button type="button" class="ngx-editor-button" *ngIf="canEnableToolbarOptions('link')" popoverTitle="Insert Link" [popover]="insertLinkTemplate"
title="Insert/Edit Link" #urlPopover="bs-popover" [disabled]="!config['enableToolbar']">
<i class="fa fa-link" aria-hidden="true"></i>
</button>
<button type="button" class="ngx-editor-button" *ngIf="canEnableToolbarOptions('unlink')" (click)="triggerCommand('unlink')"
Expand All @@ -124,3 +124,23 @@
</button>
</div>
</div>

<!-- Popover templates -->
<ng-template #insertLinkTemplate>
<form class="ngxe-popover-form" [formGroup]="urlForm" (ngSubmit)="urlForm.valid && insertLink()" autocomplete="off">
<div class="form-group">
<label for="urlInput" class="small">URL</label>
<input type="text" class="form-control-sm" id="URLInput" aria-describedby="emailHelp" placeholder="URL" formControlName="urlLink"
required>
</div>
<div class="form-group">
<label for="urlTextInput" class="small">Text</label>
<input type="text" class="form-control-sm" id="urlTextInput" placeholder="Text" formControlName="urlText" required>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="urlNewTab" formControlName="urlNewTab">
<label class="form-check-label" for="urlNewTab">Open in new tab</label>
</div>
<button type="submit" class="btn-primary btn-sm btn">Submit</button>
</form>
</ng-template>
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,41 @@
}
}
}

::ng-deep .ngxe-popover-form {
min-width: 15rem;

.form-group {
label {
display: none;
margin: 0;
}

.form-control-sm {
width: 100%;
outline: none;
border: none;
border-bottom: solid 1px #bdbdbd;
border-radius: 0;
margin-bottom: 1px;
padding-left: 0;
padding-right: 0;

&:focus, &:active {
border-bottom: 2px solid #1e88e5;
box-shadow: none;
margin-bottom: 0;
}
}
}

.form-check {
margin-bottom: 1rem;
}

.btn {
&:focus {
box-shadow: none !important;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { NgxEditorToolbarComponent } from './ngx-editor-toolbar.component';
import { ngxEditorConfig } from '../common/ngx-editor.defaults';
import { PopoverModule } from 'ngx-bootstrap';
import { CommandExecutorService } from '../common/services/command-executor.service';
import { MessageService } from '../common/services/message.service';

describe('NgxEditorToolbarComponent', () => {
let component: NgxEditorToolbarComponent;
let fixture: ComponentFixture<NgxEditorToolbarComponent>;

beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [NgxEditorToolbarComponent]
imports: [FormsModule, ReactiveFormsModule, PopoverModule.forRoot()],
declarations: [NgxEditorToolbarComponent],
providers: [CommandExecutorService, MessageService]
})
.compileComponents();
}));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,39 @@
import { Component, Input, Output, EventEmitter } from '@angular/core';
import { Component, Input, Output, EventEmitter, OnInit, ViewChild } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { PopoverConfig } from 'ngx-bootstrap';
import { CommandExecutorService } from '../common/services/command-executor.service';
import { MessageService } from '../common/services/message.service';
import * as Utils from '../common/utils/ngx-editor.utils';

@Component({
selector: 'app-ngx-editor-toolbar',
templateUrl: './ngx-editor-toolbar.component.html',
styleUrls: ['./ngx-editor-toolbar.component.scss']
styleUrls: ['./ngx-editor-toolbar.component.scss'],
providers: [PopoverConfig]
})

export class NgxEditorToolbarComponent {
export class NgxEditorToolbarComponent implements OnInit {

urlForm: FormGroup;

/**
* Editor configuration
*/
@Input() config: any;
@ViewChild('urlPopover') urlPopover;
/**
* Emits an event when a toolbar button is clicked
*/
@Output() execute: EventEmitter<string> = new EventEmitter<string>();

constructor() { }
constructor(private _popOverConfig: PopoverConfig,
private _formBuilder: FormBuilder,
private _messageService: MessageService,
private _commandExecutorService: CommandExecutorService) {
this._popOverConfig.outsideClick = true;
this._popOverConfig.placement = 'bottom';
this._popOverConfig.container = 'body';
}

/**
* enable or diable toolbar based on configuration
Expand All @@ -38,4 +53,33 @@ export class NgxEditorToolbarComponent {
this.execute.emit(command);
}

/**
* create URL insert form
*/
buildUrlForm(): void {
this.urlForm = this._formBuilder.group({
urlLink: ['', [Validators.required]],
urlText: ['', [Validators.required]],
urlNewTab: [true]
});
}

/**
* insert link
*/
insertLink() {

try {
this._commandExecutorService.createLink(this.urlForm.value);
} catch (error) {
this._messageService.sendMessage(error.message);
}

this.urlPopover.hide();
}

ngOnInit() {
this.buildUrlForm();
}

}
4 changes: 3 additions & 1 deletion src/app/ngx-editor/ngx-editor.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { NgxEditorComponent } from './ngx-editor.component';
import { NgxGrippieComponent } from './ngx-grippie/ngx-grippie.component';
import { NgxEditorToolbarComponent } from './ngx-editor-toolbar/ngx-editor-toolbar.component';
import { NgxEditorMessageComponent } from './ngx-editor-message/ngx-editor-message.component';
import { PopoverModule } from 'ngx-bootstrap';
import { MessageService } from './common/services/message.service';
import { CommandExecutorService } from './common/services/command-executor.service';

Expand All @@ -13,6 +14,7 @@ describe('NgxEditorComponent', () => {

beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [FormsModule, ReactiveFormsModule, PopoverModule.forRoot()],
providers: [MessageService, CommandExecutorService],
declarations: [NgxEditorComponent,
NgxGrippieComponent,
Expand Down
3 changes: 3 additions & 0 deletions src/app/ngx-editor/ngx-editor.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,9 @@ export class NgxEditorComponent implements OnInit, ControlValueAccessor {

onBlur(): void {

/** save selection if focussed out */
this._commandExecutor.savedSelection = Utils.saveSelection();

if (typeof this.onTouched === 'function') {
this.onTouched();
}
Expand Down
10 changes: 4 additions & 6 deletions src/app/ngx-editor/ngx-editor.module.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';

// components
import { ReactiveFormsModule } from '@angular/forms';
import { PopoverModule } from 'ngx-bootstrap';
import { NgxEditorComponent } from './ngx-editor.component';
import { NgxGrippieComponent } from './ngx-grippie/ngx-grippie.component';
import { NgxEditorMessageComponent } from './ngx-editor-message/ngx-editor-message.component';
import { NgxEditorToolbarComponent } from './ngx-editor-toolbar/ngx-editor-toolbar.component';

// services
import { MessageService } from './common/services/message.service';
import { CommandExecutorService } from './common/services/command-executor.service';

@NgModule({
imports: [CommonModule, FormsModule],
imports: [CommonModule, FormsModule, ReactiveFormsModule, PopoverModule.forRoot()],
declarations: [NgxEditorComponent, NgxGrippieComponent, NgxEditorMessageComponent, NgxEditorToolbarComponent],
exports: [NgxEditorComponent],
exports: [NgxEditorComponent, PopoverModule],
providers: [CommandExecutorService, MessageService]
})

Expand Down

0 comments on commit d4001c4

Please sign in to comment.