Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(icons): add support for custom icons in menu #455

Merged
merged 5 commits into from
Feb 26, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 6 additions & 13 deletions docs/src/content/docs/en/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,7 @@ Then in HTML
```html
<div class="NgxEditor__Wrapper">
<ngx-editor-menu [editor]="editor"> </ngx-editor-menu>
<ngx-editor
[editor]="editor"
[ngModel]="html"
[disabled]="false"
[placeholder]="'Type here...'"
></ngx-editor>
<ngx-editor [editor]="editor" [ngModel]="html" [disabled]="false" [placeholder]="'Type here...'"></ngx-editor>
</div>
```

Expand All @@ -89,19 +84,14 @@ const jsonDoc = toDoc(html);
### Commands

```ts
this.editor.commands
.textColor('red')
.insertText('Hello world!')
.focus()
.scrollIntoView()
.exec();
this.editor.commands.textColor('red').insertText('Hello world!').focus().scrollIntoView().exec();
```

Run `exec` to apply the changes to the editor.

### Optional Configuration

You can specify locals to be used in the editor
You can specify locals and icons to be used in the editor

```ts
NgxEditorModule.forRoot({
Expand All @@ -112,6 +102,9 @@ NgxEditorModule.forRoot({
underline: 'Underline',
// ...
},
icons: {
bold: '<img src="https://cdn-icons-png.flaticon.com/512/1827/1827924.png " width="15" height="15" alt="" title="" class="img-small">',
jorgeucano marked this conversation as resolved.
Show resolved Hide resolved
},
});
```

Expand Down
51 changes: 39 additions & 12 deletions docs/src/content/docs/en/menu.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,7 @@ export class AppComponent implements OnInit, OnDestroy {
**component.html**

```html
<ngx-editor-menu
[editor]="editor"
[toolbar]="toolbar"
[colorPresets]="colorPresets"
>
</ngx-editor-menu>
<ngx-editor-menu [editor]="editor" [toolbar]="toolbar" [colorPresets]="colorPresets"> </ngx-editor-menu>
```

## Custom Menu
Expand All @@ -62,12 +57,7 @@ Note: The input is just a `TemplateRef`, the menu component will render whatever
#### Editor

```html
<ngx-editor-menu
[editor]="editor"
[toolbar]="toolbar"
[customMenuRef]="customMenu"
>
</ngx-editor-menu>
<ngx-editor-menu [editor]="editor" [toolbar]="toolbar" [customMenuRef]="customMenu"> </ngx-editor-menu>

<!-- Create template reference -->
<ng-template #customMenu>
Expand Down Expand Up @@ -172,3 +162,40 @@ OR

- **editor** - (`Required`) editor instance
- **autoPlace** - (`Optional`) positions automatically to the top or bottom based on the space available. `false` by default

## Custom Icons in ngx-editor-menu

By default ngx-editor add a list of google font icons, but have an option for replace with the icons you need.
Support svg/img/tags/etc, have the option for modify one or more icons.
You can specify the icons in the forRoot

```ts
NgxEditorModule.forRoot({
icons: {
bold: '<img src="https://cdn-icons-png.flaticon.com/512/1827/1827924.png " width="15" height="15" alt="" title="" class="img-small">',
},
});
```

The list of icon you can change is:

- align_center
- align_justify
- align_left
- align_right
- bold
- bullet_list
- code
- color_fill
- format_clear
- horizontal_rule
- image
- italic
- link
- ordered_list
- quote
- strike
- text_color
- underline
- unlink
- path
43 changes: 43 additions & 0 deletions projects/ngx-editor/src/lib/Icons.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/* eslint-disable @typescript-eslint/quotes */
export const defaults: Record<string, string> = {
// menu
align_center: `<path d="M7 15v2h10v-2H7zm-4 6h18v-2H3v2zm0-8h18v-2H3v2zm4-6v2h10V7H7zM3 3v2h18V3H3z"/>`,
align_justify: `<path d="M3 21h18v-2H3v2zm0-4h18v-2H3v2zm0-4h18v-2H3v2zm0-4h18V7H3v2zm0-6v2h18V3H3z"/>`,
align_left: `<path d="M15 15H3v2h12v-2zm0-8H3v2h12V7zM3 13h18v-2H3v2zm0 8h18v-2H3v2zM3 3v2h18V3H3z"/>`,
align_right: `<path d="M3 21h18v-2H3v2zm6-4h12v-2H9v2zm-6-4h18v-2H3v2zm6-4h12V7H9v2zM3 3v2h18V3H3z"/>`,
bold: `<path d="M15.6 10.79c.97-.67 1.65-1.77 1.65-2.79 0-2.26-1.75-4-4-4H7v14h7.04c2.09 0 3.71-1.7 3.71-3.79 0-1.52-.86-2.82-2.15-3.42zM10 6.5h3c.83 0 1.5.67 1.5 1.5s-.67 1.5-1.5 1.5h-3v-3zm3.5 9H10v-3h3.5c.83 0 1.5.67 1.5 1.5s-.67 1.5-1.5 1.5z" />`,
bullet_list: `<path d="M4 10.5c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm0-6c-.83 0-1.5.67-1.5 1.5S3.17 7.5 4 7.5 5.5 6.83 5.5 6 4.83 4.5 4 4.5zm0 12c-.83 0-1.5.68-1.5 1.5s.68 1.5 1.5 1.5 1.5-.68 1.5-1.5-.67-1.5-1.5-1.5zM7 19h14v-2H7v2zm0-6h14v-2H7v2zm0-8v2h14V5H7z"/>`,
code: `<path d="M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0l4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z"/>`,
color_fill: `<path d="M16.56,8.94L7.62,0L6.21,1.41l2.38,2.38L3.44,8.94c-0.59,0.59-0.59,1.54,0,2.12l5.5,5.5C9.23,16.85,9.62,17,10,17 s0.77-0.15,1.06-0.44l5.5-5.5C17.15,10.48,17.15,9.53,16.56,8.94z M5.21,10L10,5.21L14.79,10H5.21z M19,11.5c0,0-2,2.17-2,3.5 c0,1.1,0.9,2,2,2s2-0.9,2-2C21,13.67,19,11.5,19,11.5z M2,20h20v4H2V20z"/>`,
format_clear: `<path d="M0 0h24v24H0z" fill="none"/><path d="M3.27 5L2 6.27l6.97 6.97L6.5 19h3l1.57-3.66L16.73 21 18 19.73 3.55 5.27 3.27 5zM6 5v.18L8.82 8h2.4l-.72 1.68 2.1 2.1L14.21 8H20V5H6z"/>`,
horizontal_rule: `<g>
<rect fill="none" fill-rule="evenodd" height="24" width="24"/>
<rect fill-rule="evenodd" height="2" width="16" x="4" y="11"/>
</g>`,
image: `<path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/>`,
italic: `<path d="M10 4v3h2.21l-3.42 8H6v3h8v-3h-2.21l3.42-8H18V4z" />`,
link: `<path d="M0 0h24v24H0z" fill="none"/><path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"/>`,
ordered_list: `<path d="M2 17h2v.5H3v1h1v.5H2v1h3v-4H2v1zm1-9h1V4H2v1h1v3zm-1 3h1.8L2 13.1v.9h3v-1H3.2L5 10.9V10H2v1zm5-6v2h14V5H7zm0 14h14v-2H7v2zm0-6h14v-2H7v2z"/>`,
quote: `<path d="M0 0h24v24H0z" fill="none"/><path d="M6 17h3l2-4V7H5v6h3zm8 0h3l2-4V7h-6v6h3z"/>`,
strike: `<path d="M6.85,7.08C6.85,4.37,9.45,3,12.24,3c1.64,0,3,0.49,3.9,1.28c0.77,0.65,1.46,1.73,1.46,3.24h-3.01 c0-0.31-0.05-0.59-0.15-0.85c-0.29-0.86-1.2-1.28-2.25-1.28c-1.86,0-2.34,1.02-2.34,1.7c0,0.48,0.25,0.88,0.74,1.21 C10.97,8.55,11.36,8.78,12,9H7.39C7.18,8.66,6.85,8.11,6.85,7.08z M21,12v-2H3v2h9.62c1.15,0.45,1.96,0.75,1.96,1.97 c0,1-0.81,1.67-2.28,1.67c-1.54,0-2.93-0.54-2.93-2.51H6.4c0,0.55,0.08,1.13,0.24,1.58c0.81,2.29,3.29,3.3,5.67,3.3 c2.27,0,5.3-0.89,5.3-4.05c0-0.3-0.01-1.16-0.48-1.94H21V12z"/>`,
text_color: `<path d="M2,20h20v4H2V20z M5.49,17h2.42l1.27-3.58h5.65L16.09,17h2.42L13.25,3h-2.5L5.49,17z M9.91,11.39l2.03-5.79h0.12l2.03,5.79 H9.91z"/>`,
underline: `<path d="M12 17c3.31 0 6-2.69 6-6V3h-2.5v8c0 1.93-1.57 3.5-3.5 3.5S8.5 12.93 8.5 11V3H6v8c0 3.31 2.69 6 6 6zm-7 2v2h14v-2H5z"/>`,
unlink: `<path d="M17 7h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1 0 1.43-.98 2.63-2.31 2.98l1.46 1.46C20.88 15.61 22 13.95 22 12c0-2.76-2.24-5-5-5zm-1 4h-2.19l2 2H16zM2 4.27l3.11 3.11C3.29 8.12 2 9.91 2 12c0 2.76 2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1 0-1.59 1.21-2.9 2.76-3.07L8.73 11H8v2h2.73L13 15.27V17h1.73l4.01 4L20 19.74 3.27 3 2 4.27z"/>`,
path: `<path></path>`,
};

export type IconsKeys = keyof typeof defaults;

class Icons {
icons = defaults;

constructor(newIcons: Partial<Record<IconsKeys, string>> = {}) {
this.icons = { ...defaults, ...newIcons };
}

get = (key: string):string => {
return this.icons[key] ?? '';
};
}

export default Icons;
1 change: 1 addition & 0 deletions projects/ngx-editor/src/lib/editor-config.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ import { Injectable } from '@angular/core';
})
export class NgxEditorServiceConfig {
public locals = {};
public icons = {};
}
2 changes: 2 additions & 0 deletions projects/ngx-editor/src/lib/editor.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { CommonModule } from '@angular/common';

import { NgxEditorConfig } from './types';
import { defaults as defaultLocals } from './Locals';
import { defaults as icons } from './Icons';

import { NgxEditorComponent } from './editor.component';
import { NgxEditorServiceConfig } from './editor-config.service';
Expand All @@ -17,6 +18,7 @@ export const NGX_EDITOR_CONFIG_TOKEN = new InjectionToken<NgxEditorConfig>('NgxE

const defaultConfig: NgxEditorConfig = {
locals: defaultLocals,
icons,
};

@NgModule({
Expand Down
6 changes: 6 additions & 0 deletions projects/ngx-editor/src/lib/editor.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Injectable, Optional } from '@angular/core';
import { NgxEditorConfig } from './types';
import Locals from './Locals';
import { NgxEditorServiceConfig } from './editor-config.service';
import Icon from './icons/index';

@Injectable({
providedIn: 'root',
Expand All @@ -17,10 +18,15 @@ export class NgxEditorService {
get locals(): Locals {
return new Locals(this.config.locals);
}

geticon(icon: string): string {
return this.config.icons[icon] ? this.config.icons[icon] : Icon.get(icon);
}
}

export const provideMyServiceOptions = (config?: NgxEditorConfig): NgxEditorServiceConfig => {
return {
locals: config.locals ?? {},
icons: config.icons ?? {},
};
};
92 changes: 51 additions & 41 deletions projects/ngx-editor/src/lib/icons/index.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,30 @@
/* eslint-disable @typescript-eslint/naming-convention */
// Icons source: https://material.io/

import bold from './bold';
import italic from './italic';
import code from './code';
import underline from './underline';
import strike from './strike';
import orderedList from './ordered_list';
import bulletList from './bullet_list';
import quote from './quote';
import link from './link';
import unlink from './unlink';
import image from './image';
import alignLeft from './align_left';
import alignCenter from './align_center';
import alignRight from './align_right';
import alignJustify from './align_justify';
import textColor from './text_color';
import colorFill from './color_fill';
import horizontalRule from './horizontal_rule';
import formatClear from './format_clear';
import Icons from '../Icons';

const ICON_LIST = new Icons();

const bold = ICON_LIST.get('bold');
const italic = ICON_LIST.get('italic');
const code = ICON_LIST.get('code');
const underline = ICON_LIST.get('underline');
const strike = ICON_LIST.get('strike');
const ordered_list = ICON_LIST.get('ordered_list');
const bullet_list = ICON_LIST.get('bullet_list');
const quote = ICON_LIST.get('quote');
const link = ICON_LIST.get('link');
const unlink = ICON_LIST.get('unlink');
const image = ICON_LIST.get('image');
const align_left = ICON_LIST.get('align_left');
const align_center = ICON_LIST.get('align_center');
const align_right = ICON_LIST.get('align_right');
const align_justify = ICON_LIST.get('align_justify');
const text_color = ICON_LIST.get('text_color');
const color_fill = ICON_LIST.get('color_fill');
const horizontal_rule = ICON_LIST.get('horizontal_rule');
const format_clear = ICON_LIST.get('format_clear');
const path = ICON_LIST.get('path');

const DEFAULT_ICON_HEIGHT = 20;
const DEFAULT_ICON_WIDTH = 20;
Expand All @@ -30,36 +36,40 @@ const icons: Record<string, any> = {
code,
underline,
strike,
ordered_list: orderedList,
bullet_list: bulletList,
ordered_list,
bullet_list,
blockquote: quote,
link,
unlink,
image,
align_left: alignLeft,
align_center: alignCenter,
align_right: alignRight,
align_justify: alignJustify,
text_color: textColor,
color_fill: colorFill,
horizontal_rule: horizontalRule,
format_clear: formatClear,
align_left,
align_center,
align_right,
align_justify,
text_color,
color_fill,
horizontal_rule,
format_clear,
path,
};

class Icon {
static get(name: keyof typeof icons, fill = DEFAULT_ICON_FILL): string {
const path = icons[name] || '<path></path>';
return `
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill=${fill}
height=${DEFAULT_ICON_HEIGHT}
width=${DEFAULT_ICON_WIDTH}
>
${path}
</svg>
`;
const fullPath = icons[name];
if (fullPath && (fullPath.includes('<path') || fullPath.includes('<g'))) {
return `--
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill=${fill}
height=${DEFAULT_ICON_HEIGHT}
width=${DEFAULT_ICON_WIDTH}
>
${fullPath}
</svg>
`;
}
return fullPath;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { EditorView } from 'prosemirror-view';
import { Subscription } from 'rxjs';

import Editor from '../../../Editor';
import Icon from '../../../icons';
import { TBItems } from '../../../types';
import { SanitizeHtmlPipe } from '../../../pipes/sanitize/sanitize-html.pipe';
import { ToggleCommands } from '../MenuCommands';
Expand Down Expand Up @@ -50,8 +49,7 @@ export class BubbleComponent implements OnInit, OnDestroy {
];

getIcon(name: TBItems): SafeHtml {
const icon = Icon.get(name);
return this.sanitizeHTML.transform(icon);
return this.sanitizeHTML.transform(this.ngxeService.geticon(name));
}

getTitle(name: string): string {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
import { EditorView } from 'prosemirror-view';
import { Subscription } from 'rxjs';

import Icon from '../../../icons';
import Icon from '../../../icons/index';
jorgeucano marked this conversation as resolved.
Show resolved Hide resolved
import { NgxEditorService } from '../../../editor.service';
import { MenuService } from '../menu.service';
import { TextColor, TextBackgroundColor } from '../MenuCommands';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { Subscription } from 'rxjs';

import { NgxEditorService } from '../../../editor.service';
import { MenuService } from '../menu.service';
import Icon from '../../../icons';
import Icon from '../../../icons/index';
import { Image as ImageCommand } from '../MenuCommands';

@Component({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { EditorView } from 'prosemirror-view';
import { Subscription } from 'rxjs';

import Icon from '../../../icons';
import { InsertCommands } from '../MenuCommands';
import { NgxEditorService } from '../../../editor.service';
import { MenuService } from '../menu.service';
import { TBItems, ToolbarItem } from '../../../types';
import icons from '../../../icons/index';

@Component({
selector: 'ngx-insert-command',
Expand Down Expand Up @@ -54,7 +54,7 @@ export class InsertCommandComponent implements OnInit, OnDestroy {
}

ngOnInit(): void {
this.html = Icon.get(this.name);
this.html = icons.get(this.name);

this.editorView = this.menuService.editor.view;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { EditorView } from 'prosemirror-view';
import { Subscription } from 'rxjs';

import { NgxEditorService } from '../../../editor.service';
import Icon from '../../../icons';
import Icon from '../../../icons/index';
import { MenuService } from '../menu.service';
import { Link as LinkCommand } from '../MenuCommands';

Expand Down
Loading