Giter VIP home page Giter VIP logo

ngx-wig's Introduction

ngx-wig

Build Status

screen shot 2017-12-12 at 14 52 51

Dependencies

it's only Angular! No jQuery or other WYSIWYG monsters

Angular Support (older than the latest version)

For Angular 16 [email protected]

For Angular 15 [email protected]

For Angular 14 [email protected]

For Angular 13 [email protected]

...

Icons

Icons are not in the pack! You can use the icons that you like. We recommend to use Material Design Icons

If you do not want to use a full icons set, you can use these steps:

  1. go to icons set
  2. choose the icon that you want, press the right mouse button on it, and then select the "View SVG" option
  3. go to URL-encoder for SVG and use it to convert your SVG

Installation

ngx-wig could be simply installed via npm:

npm install ngx-wig

Usage

First, import the ngx-wig to your module:

import {NgxWigModule} from 'ngx-wig';

@NgModule({
  imports: [ NgxWigModule ]
});

it's just an attribute directive for textarea:

<link href="https://cdn.materialdesignicons.com/2.1.19/css/materialdesignicons.min.css" rel="stylesheet" />
...
<ngx-wig [content]="text1"></ngx-wig>

Examples

Quick start ( demo )

<ngx-wig [content]="text1"></ngx-wig>

Placeholder ( demo )

<ngx-wig [content]="text1" [placeholder]="'Enter instructions here.'"></ngx-wig>

ngModel sync ( demo )

<ngx-wig [(ngModel)]="text1"></ngx-wig>
<ngx-wig [(ngModel)]="text1"></ngx-wig>

Set buttons ( demo )

<ngx-wig [content]="text1" [buttons]="'bold, italic'"></ngx-wig>

onContentChange Hook ( demo )

<ngx-wig [content]="text1" (contentChange)="result = $event"></ngx-wig>
<div [innerHTML]="result"></div>

Reactive FormControl ( demo )

<ngx-wig [formControl]="text1"></ngx-wig>

Adding own buttons

Please check an example here https://stackblitz.com/edit/ngx-wig-sample-plugins?file=src/app.ts

Filtering/removing extra styles on paste ( demo )

providers: [{ provide: NgxWigFilterService, useClass: NgxWigFilterStylesService}]

Development

To generate all *.js, *.d.ts and *.metadata.json files:

$ npm run build

To lint all *.ts files:

$ npm run lint

To run all tests:

$ npm run test

License

MIT © Stepan Suvorov

ngx-wig's People

Contributors

0xff00ff avatar alexey-shakura avatar alextrashkov avatar bampakoa avatar bolelamx avatar borisbeast avatar dependabot[bot] avatar jpercyetech avatar kalski avatar mgrinko avatar mpapini91 avatar mpapst avatar smiranin avatar stevermeister avatar svolianskyi avatar volodziu avatar yuriykuzin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ngx-wig's Issues

Does it support angular 4.3?

Does it support angular 4.3?

I can't built aot in my project.
I love this No jQuery editor.

Error: Metadata version mismatch for module aot/node_modules/ngx-wig/ngx-wig.d.ts, found version 4, expected 3
npm ERR! peer dep missing: @angular/core@^5.0.0, required by [email protected]
npm ERR! peer dep missing: rxjs@^5.5.2, required by [email protected]
npm ERR! extraneous: @mdi/[email protected] 

Thank U :D

Colaborate

Hi, I want to collaborate with your component, but I don't know exactly how.
I have done a few of changes:
One to get a new button for the font size
and the other one to translate to Spanish.
If you like my solution, or you want to improve I wold be proud of it.
I send the modifieds files.
Thanks

`import {
Component,
ElementRef,
EventEmitter,
Input,
OnChanges,
OnInit,
Output,
SimpleChanges,
ViewChild,
ViewEncapsulation,
forwardRef,
} from '@angular/core';

import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';
import { NgxWigToolbarService, TButton } from './ngx-wig-toolbar.service';

@component({
selector: 'ngx-wig',
templateUrl: './ngx-wig.html',
styleUrls: ['./ngx-wig.css'],
providers: [
NgxWigToolbarService,
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => NgxWigComponent),
multi: true
}
],
encapsulation: ViewEncapsulation.None
})
export class NgxWigComponent implements OnInit, OnChanges, ControlValueAccessor {

@input()
public content: string;

@input()
public placeholder: string;

@input()
public buttons: string;

@input()
public disabled: boolean;

@input()
public language: string = 'en';

@input()
public isSourceModeAllowed = false;

@output()
public contentChange = new EventEmitter();

@ViewChild('ngWigEditable')
public ngxWigEditable: ElementRef;

public editMode = false;
public container: HTMLElement;
public toolbarButtons: TButton[] = [];
public hasFocus = false;
public iconsTheme: string;

public constructor(
private _ngWigToolbarService: NgxWigToolbarService
) {
// hardcoded icons theme for now
this.iconsTheme = nw-button-mdi;
}

public toggleEditMode(): void {
this.editMode = !this.editMode;
}

public execCommand(command: string, options: string): boolean {
if (this.editMode) {
return false;
}
if (document.queryCommandSupported && !document.queryCommandSupported(command)) {
throw 'The command "' + command + '" is not supported';
}
if (command === 'createlink' || command === 'insertImage' || command === 'fontSize') {
if (command === 'fontSize') {
options = window.prompt(this._ngWigToolbarService.resource().enterURL, '3');
if (options != '1' && options != '2' && options != '3' && options != '4' &&
options != '5' && options != '6' && options != '7')
return;
}
else {
options = window.prompt(this._ngWigToolbarService.resource().enterURL, 'http://');
if (!options) {
return;
}
}
}

this.container.focus();

// use insertHtml for `createlink` command to account for IE/Edge purposes, in case there is no selection
let selection = document.getSelection().toString();
if (command === 'createlink' && selection === '') {
  document.execCommand('insertHtml', false, '<a href="' + options + '">' + options + '</a>');
} else {
  document.execCommand(command, false, options);
}

this._onContentChange(this.container.innerHTML);

}

public ngOnInit(): void {
this._ngWigToolbarService.language(this.language);
this.toolbarButtons = this._ngWigToolbarService.getToolbarButtons(this.buttons);
this.container = this.ngxWigEditable.nativeElement;

if (this.content) {
  this.container.innerHTML = this.content;
}

}

private _onContentChange(newContent: string): void {
this.content = newContent;
this.contentChange.emit(this.content);
this.propagateChange(this.content);
}

public ngOnChanges(changes: SimpleChanges): void {
if (this.container && changes['content']) {
// clear the previous content
this.container.innerHTML = '';

  // add the new content
  this.pasteHtmlAtCaret(changes['content'].currentValue);
}

}

public onTextareaChange(newContent: string): void {
// model -> view
this.container.innerHTML = newContent;
this._onContentChange(newContent);
}

public writeValue(value: any): void {
if (!value) { value = ''; }

this.container.innerHTML = value;
this._onContentChange(value);

}

public shouldShowPlaceholder(): boolean {
return this.placeholder
&& !this.container.innerText;
}

private pasteHtmlAtCaret(html) {
let sel, range;

if (window.getSelection) {
  sel = window.getSelection();

  if (sel.getRangeAt && sel.rangeCount) {
    range = sel.getRangeAt(0);
    range.deleteContents();

    // append the content in a temporary div
    let el = document.createElement('div');
    el.innerHTML = html;
    let frag = document.createDocumentFragment(), node, lastNode;
    while ( (node = el.firstChild) ) {
      lastNode = frag.appendChild(node);
    }
    range.insertNode(frag);

    // Preserve the selection
    if (lastNode) {
      range = range.cloneRange();
      range.setStartAfter(lastNode);
      range.collapse(true);
      sel.removeAllRanges();
      sel.addRange(range);
    }
  }
}

}

public registerOnChange(fn: any): void {
this.propagateChange = fn;
}

public registerOnTouched(fn: () => void): void {
this.propagateTouched = fn;
}

private propagateChange: any = (_: any) => { };
public propagateTouched = () => {};

onBlur() {
this.hasFocus = false;
this.propagateTouched();
}

setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
}
}
export type TButton = {
title?: string,
command?: string,
styleClass?: string,
pluginName?: string,
isComplex?: boolean
};

export type TResource = {
language: string,
unorderedList: string,
orderedList: string,
bold: string,
italic: string,
link: string,
underline: string,
fontSize: string,
enterURL: string,
insertFontSize:string
}

export type TButtonLibrary = {
[name: string]: TButton
};

export class NgxWigToolbarService {
private _resources: TResource[];
private getResources(): TResource[] {
return this._resources ||
(this._resources = [{
language: 'en',
unorderedList: 'Unordered List',
orderedList: 'Ordered List',
bold: 'Bold',
italic: 'Italic',
link: 'Link',
underline: 'Underline',
fontSize: 'Font Size',
enterURL: 'Please enter the URL',
insertFontSize: 'Insert font size from 1 to 7'
},
{
language: 'es',
unorderedList: 'Lista de puntos',
orderedList: 'Lista ordenada',
bold: 'Negrita',
italic: 'Cursiva',
link: 'Link',
underline: 'Subrayado',
fontSize: 'Tamaño fuente',
enterURL: 'Introduzca la dirección URL',
insertFontSize: 'Introduzca el tamaño de la fuente del 1 al 7'
}
]);
}
private _resource: TResource;
public resource(): TResource {
return this._resource || (this._resource = this.getResources().find(c => c.language == (this._language || 'en')) ||
this.getResources().find(c => c.language == 'en'));
}
private _language
public language(l: string) {
this._language = l;
this._resource = undefined;
}

private __buttonLibrary: TButtonLibrary;
private _buttonLibrary(): TButtonLibrary {
return this.__buttonLibrary || (this.__buttonLibrary = {
list1: { title: this.resource().unorderedList, command: 'insertunorderedlist', styleClass: 'list-ul' },
list2: { title: this.resource().orderedList, command: 'insertorderedlist', styleClass: 'list-ol' },
bold: { title: this.resource().bold, command: 'bold', styleClass: 'bold' },
italic: { title: this.resource().italic, command: 'italic', styleClass: 'italic' },
link: { title: this.resource().link, command: 'createlink', styleClass: 'link' },
underline: { title: this.resource().underline, command: 'underline', styleClass: 'format-underlined' },
fontsize: { title: this.resource().fontSize, command: 'fontSize', styleClass: 'font-size' }
});
}

private _defaultButtonsList = ['list1', 'list2', 'bold', 'italic', 'link', 'underline', 'fontsize'];
public setButtons(buttons: string[]): void {
// if(!angular.isArray(buttons)) {
// throw 'Argument "buttons" should be an array';
// }

this._defaultButtonsList = buttons;

};

public addStandardButton(
name: string,
title: string,
command: string,
styleClass: string
) {

if (!name || !title || !command) {
  throw 'Arguments "name", "title" and "command" are required';
}

styleClass = styleClass || '';
this._buttonLibrary()[name] = {title: title, command: command, styleClass: styleClass};
this._defaultButtonsList.push(name);

}

public addCustomButton(name: string, pluginName: string): void {
if (!name || !pluginName) {
throw 'Arguments "name" and "pluginName" are required';
}

this._buttonLibrary()[name] = {pluginName: pluginName, isComplex: true};
this._defaultButtonsList.push(name);

}

public getToolbarButtons(buttonsList?: string): {}[] {
let buttons = this._defaultButtonsList;
const toolbarButtons: TButton[] = [];

if (typeof buttonsList !== 'undefined') {
  buttons = string2array(buttonsList);
}

buttons.forEach(buttonKey => {
  if (!buttonKey) {
    return;
  }

  if (!this._buttonLibrary()[buttonKey]) {
    throw 'There is no "' + buttonKey + '" in your library. Possible variants: ' + Object.keys(this._buttonLibrary);
  }

  let button = Object.assign({}, this._buttonLibrary()[buttonKey]);
  // button.isActive = () => {return !!this.command && document.queryCommandState(this.command);}
  toolbarButtons.push(button);
});

return toolbarButtons;

}

}

function string2array(keysString: string) {
return keysString.split(',').map(Function.prototype.call, String.prototype.trim);
}
.nw-button-fa.font-size:before {
content: '\f27f';
}
.nw-button-mdi.font-size:before {
content: '\f27f';
}`

Metadata mismatch

When I try to compile my project with AOT it throws:
/node_modules/ngx-wig/ngx-wig.d.ts, found version 4, expected 3

normal build runs fine.

Using ngx-wig 1.0-rc7 with SystemJS 0.19 and TS 2.3.4

Basic setup doesn't display icons

Following the EXAMPLES here https://github.com/stevermeister/ngx-wig, no icons appear on the format options in the text editor

Also, the example "Set buttons": <ngx-wig [content]="text1" [buttons]="formats, bold, italic"></ngx-wig> seem to throw an error Parser Error: Unexpected token ',' at column 8 in [formats, bold, italic] i...

link with target="_blank"

Hi,

I would like to have all link create in the component with the mode target="_blank".

Is it possible ?

Awesome module, supports SSR, but...

It works out of the box with SSR (Server Side Rendering using angular 5.2.9) which makes it unique among all other wysiwyg editors I've tested so far.

I'm currently investigating which editor module to use for a new project feature and I, by far, favor ngx-wig so far. However, I have a few concerns - sorry for wording them very directly - but I hope getting them answered will benefit all potential users of ngx-wig:

  • Is development still ongoing for this project? (I see very low activity lately)
  • Are there any plans to fix the current issue with one button not showing up and the failing builds?
  • Will more simple features be added (indentation, text alignment)?

I would not mind being a contributor if this module is still planned to move forward.

Thanks :)

Can't compile with angular i18n

Hello,
I tried to implement the native i18n module in my application, but when I run ng xi18n in the terminal, I get this error:

ERROR in Unexpected value 'NgxWigModule in C:/workspace/testApp/node_modules/ngx-wig/lib/ngx-wig.module.d.ts' imported by the module 'AappModule' in C:/workspace/testApp/src/app/app.mdule.ts'. Please add a @NgModule annotation.

Any tips?
thanks

Using "&nbsp;" for 1 white space

I'm experiencing a strange problem. It seems that when pasting some content, the editor uses the special HTML string " " even when there is only one white space.

Unfortunately I'm not able to reproduce the bug as it requires pasting some text into the editor.

No icon is showing for buttons

i am using angular 6 and icons are not showing on buttons. this issue is coming in all old version including 1.6.0 and 1.0.3.
Screenshot from 2019-04-25 13-57-15

Size of the Field

Is it possible to change the size of the ngx-wig area.

I tried to do it with css :

.nw-editor {
height: 100px !important;
}

But it doesn't work as I'm doing it from another component.

Plan for Angular 8 Support?

I would like to upgrade my angular-7 project to angular-8.
Do you have a plan for upgrading it to support angular-8? If not, how can I help with it?
I have cloned the master-branch and locally upgraded to angular8. Unittests were through with small modification.
Shall I create a new branch and push it and create merge request?
Also I would like to know how I can test it with my local app(s).

Accent issue with Angular 7

I know that this editor is not Angular 7 compatible, but it works "almost" fine.

The accent doesn't work. If you want write "ú", you will get "´u". The same issue with "û" or "ü".

Error after migration to Angular 9

Do you know how to fix this issue?

ERROR in node_modules/ngx-wig/lib/ngx-wig.component.d.ts:40:18 - error TS2314: Generic type 'ɵɵFactoryDef' requires 1 type argument(s).

40 static ɵfac: i0.ɵɵFactoryDef<NgxWigComponent, never>;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
node_modules/ngx-wig/lib/ngx-wig.component.d.ts:41:18 - error TS2314: Generic type 'ɵɵComponentDefWithMeta' requires 6 type argument(s).

41 static ɵcmp: i0.ɵɵComponentDefWithMeta<NgxWigComponent, "ngx-wig", never, { "content": "content"; "placeholder": "placeholder"; "buttons": "buttons"; "disabled": "disabled"; }, { "contentChange": "contentChange"; }, never, never>;

Implement plugins mechanism

We need to answer the following questions for implementing a mechanism that will register and load plugins in the main editor:

  1. What kind of instance a plugin should be.
  2. How to register a plugin.

There is a related question in SO https://stackoverflow.com/questions/44654508/angular-creating-plugins-for-3rd-party-packages-libraries.

Possible solutions could be:

  • forRoot: used by convention to configure a module's providers
  • Component discovery and dynamically load (not so robust, needs a way to know the type of the actual plugin in order to load it)
  • A nice implementation from guys in ng-grid

Is it possible to use Google Material Icons?

Hi,

I would like to use these icons:

https://material.io/icons/

and I have this in my index.html:

<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">

In this pack the "bold" icon is called "format_bold". Is it possible to use this? I find it hard to understand from the Readme how to use other sets of icons.

White space(s) added on focus

First thanks for the great editor, it was precisely what I was looking for :-)

I am using the editor in an Angular 5 app within a reactive form and I noticed that onFocus the editor adds a white space if the value passed to the editor is === undefined || '' || null || false || 0 (even if the value to be passed has to be a string, I've tried with other non-string values to test the behaviour).

For simplicity, I've included an example: https://stackblitz.com/edit/ngx-wig-2-way-binding-jhdpay?embed=1&file=app/app.component.html.

I've forked the example from the ngModel one, and I can confirm that the issue is present also when passing the value via [content]="somevalue".

My problem is that I'm also assigning to the FormData element a required validator, and the fact that a white space is added breaks the validator as it thinks that some value has been entered.

As of now I've solved the issue by using contentChange to call a method that calls trim() on the value and then I call formData.patchValue({ myEditorValue: trimmedValue }). I have not included this code in the example for simplicity.

playground does not work

I just cloned fresh version and playground does not work: it does not find/compile/place index.js file.

@bampakoa after quick investigation I did not find the place, maybe you have an idea?

Toggle to view HTML

Are there plans to allow a button to toggle to HTML like Froala does? We are using ngx-wig now, and the user asked me to enable the "<>" button to see what is going wrong with the markup. For now, I could flip them into a text area.

header button

Hello,
is the header list (h1...h6) already implemented in ngx-wig?
How can i add heading to the text.
Thanks

Can we use table and image button?

We want to include two buttons

  1. Table: by clicking the table button user should be able to choose rows and columns of table, and finally this should insert a table.
  2. Image: User should be able to upload image, and application should upload the image in base64 format.

Please let us know if it is possible if yes how we can achieve this?

How to set focus/cursor at the end of the content or at the last position where cursor was?

Hi,
I am trying to add tags to the editor in 2 ways.

  1. How to get the cursor position and add some text to that position. Eg. If i am trying to change the ngModel from typescript the cursor position is set automatically to 0th character or start. If the user has written a paragraph and now he tries to annotate it with the hashtags given below the editor. Is there a way to insert it in between based on cursor position?? I did it to append in the end but Can it be done in between?? Demo is shown here.

  2. If user types '#' followed by tag and space, the content will be converted to a tag, where I am applying bootstrap 4 css to it. Demo is shown here. But when the ngModel is changed from the typescript the cursor position is changed to the start. I tried using #29 but the same behavior is seen. So is there any way to keep the focus of cursor where it was or the last position??

Any help on these 2 points will be really appreciated. :)

Is plugins mechanism ready yet?

First of all thank you so much for the awesome ngx-wig.
I want to add plugins for image, header, alignment and code in my editor.
Here in this issue, the checkbox of Implement Plugins Mechanism is checked .
So is it ready yet? If yes then how to use it?

Thank you :)

Show if button is currently enable

When using an example feature: bold, the bold button would ideally have some class to be able to style indicating to the user that that feature / button is currently active.

How to focus on the container ?

Hello,

As soon as I load the "ngWig" component, the focus goes to the first button on the toolbar. Instead I want the focus to be on the container so that the user can start typing in right away.

How can I achieve this ? Please suggest.

I am using version. -- [email protected]

Thanks
Adam

Creating custom buttons for version 0.3.6

Hello Team,
I am using ngx-wig 0.3.6 component in my angular 4 project.
My query is how can I create a custom buttons for my text editor like underline, font size, font family, insert table , insert image etc.
I am not finding any way to create them.
Also, I can't upgrade the version as the above versions are not supported with my project.
Can you please help me?

Broken in Angular 5.0

Found this attempting to upgrade ng4.2 application to ng5.

Can recreate: Clean ng cli generated template, then upgrade to angular 5. Add ngx-wig as dependency, import into the module. Several build warning but breaking during application init.

compiler.js:466 Uncaught Error: Unexpected value 'Ng2WigModule' imported by the module 'AppModule'. Please add a @NgModule annotation. at syntaxError (compiler.js:466) at compiler.js:15088 at Array.forEach (<anonymous>) at CompileMetadataResolver.webpackJsonp.../../../compiler/esm5/compiler.js.CompileMetadataResolver.getNgModuleMetadata (compiler.js:15071) at JitCompiler.webpackJsonp.../../../compiler/esm5/compiler.js.JitCompiler._loadModules (compiler.js:33484) at JitCompiler.webpackJsonp.../../../compiler/esm5/compiler.js.JitCompiler._compileModuleAndComponents (compiler.js:33445) at JitCompiler.webpackJsonp.../../../compiler/esm5/compiler.js.JitCompiler.compileModuleAsync (compiler.js:33361) at CompilerImpl.webpackJsonp.../../../platform-browser-dynamic/esm5/platform-browser-dynamic.js.CompilerImpl.compileModuleAsync (platform-browser-dynamic.js:230) at PlatformRef.webpackJsonp.../../../core/esm5/core.js.PlatformRef.bootstrapModule (core.js:5443) at Object.../../../../../src/main.ts (main.ts:11)

Is integration with ng5 in the roadmap for this project?

Type 'ModuleWithProviders' is not generic.

i am getting this error in angular 6

ERROR in node_modules/ngx-wig/lib/ngx-wig.module.d.ts(7,9): error TS2315: Type 'ModuleWithProviders' is not generic.
node_modules/ngx-wig/lib/ngx-wig.module.d.ts(8,24): error TS2315: Type 'ModuleWithProviders' is not generic.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.