Skip to content

Commit

Permalink
feat: add floating menu
Browse files Browse the repository at this point in the history
  • Loading branch information
sibiraj-s committed Feb 4, 2021
1 parent 353e657 commit 102ce62
Show file tree
Hide file tree
Showing 9 changed files with 356 additions and 10 deletions.
1 change: 0 additions & 1 deletion src/lib/editor.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
border-radius: 4px;
border: 1px solid rgba(0, 0, 0, 0.2);
position: relative;
overflow: hidden;
}

.NgxEditor--Disabled {
Expand Down
4 changes: 1 addition & 3 deletions src/lib/editor.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { DebugElement } from '@angular/core';

import { NgxEditorComponent } from './editor.component';
import { MenuModule } from './modules/menu/menu.module';
// import { BubbleComponent } from './components/bubble/bubble.component';
import Editor from './Editor';

describe('NgxEditorComponent', () => {
Expand All @@ -17,8 +16,7 @@ describe('NgxEditorComponent', () => {
MenuModule
],
declarations: [
NgxEditorComponent,
// BubbleComponent
NgxEditorComponent
]
}).compileComponents();
});
Expand Down
34 changes: 28 additions & 6 deletions src/lib/editor.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@ import {
Component, ViewChild, ElementRef,
forwardRef, OnDestroy, ViewEncapsulation,
OnInit, Output, EventEmitter,
Input, Renderer2, SimpleChanges, OnChanges, Injector,
Input, Renderer2, SimpleChanges,
OnChanges, Injector, AfterViewInit,
} from '@angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';
import { createCustomElement } from '@angular/elements';
import { createCustomElement, NgElement, WithProperties } from '@angular/elements';
import { Subscription } from 'rxjs';

import * as plugins from './plugins';
import { toHTML } from './parsers';
import Editor from './Editor';
import { ImageViewComponent } from './components/image-view/image-view.component';
import { FloatingMenuComponent } from './modules/menu/floating-menu/floating-menu.component';

@Component({
selector: 'ngx-editor',
Expand All @@ -25,7 +27,7 @@ import { ImageViewComponent } from './components/image-view/image-view.component
encapsulation: ViewEncapsulation.None
})

export class NgxEditorComponent implements ControlValueAccessor, OnInit, OnChanges, OnDestroy {
export class NgxEditorComponent implements ControlValueAccessor, OnInit, AfterViewInit, OnChanges, OnDestroy {
constructor(
private renderer: Renderer2,
private injector: Injector
Expand Down Expand Up @@ -89,12 +91,19 @@ export class NgxEditorComponent implements ControlValueAccessor, OnInit, OnChang
}

private registerCustomElements(): void {
const imgViewExists = customElements.get('ngx-image-view');
const imgViewComponent = customElements.get('ngx-image-view');

if (!imgViewExists) {
if (!imgViewComponent) {
const ImageViewElement = createCustomElement(ImageViewComponent, { injector: this.injector });
customElements.define('ngx-image-view', ImageViewElement);
}

const floatingMenuComponent = customElements.get('ngx-floating-menu');

if (!floatingMenuComponent) {
const FloatingMenuElement = createCustomElement(FloatingMenuComponent, { injector: this.injector });
customElements.define('ngx-floating-menu', FloatingMenuElement);
}
}

private registerPlugins(): void {
Expand All @@ -121,6 +130,15 @@ export class NgxEditorComponent implements ControlValueAccessor, OnInit, OnChang
this.editor.registerPlugin(plugins.image(this.injector));
}

private createFloatingMenu(): void {
type FLoatingMenuElement = NgElement & WithProperties<FloatingMenuComponent>;
const floatingMenu = this.renderer.createElement('ngx-floating-menu') as FLoatingMenuElement;

floatingMenu.editor = this.editor;

this.renderer.appendChild(this.editor.view.dom.parentElement, floatingMenu);
}

ngOnInit(): void {
if (!this.editor) {
throw new Error('NgxEditor: Required editor instance');
Expand All @@ -131,13 +149,17 @@ export class NgxEditorComponent implements ControlValueAccessor, OnInit, OnChang

this.renderer.appendChild(this.ngxEditor.nativeElement, this.editor.el);

const contentChangeSubscription = this.editor.valueChange.subscribe(jsonDoc => {
const contentChangeSubscription = this.editor.valueChanges.subscribe(jsonDoc => {
this.handleChange(jsonDoc);
});

this.subscriptions.push(contentChangeSubscription);
}

ngAfterViewInit(): void {
this.createFloatingMenu();
}

ngOnChanges(changes: SimpleChanges): void {
if (changes?.placeholder && !changes.placeholder.isFirstChange()) {
this.setPlaceholder(changes.placeholder.currentValue);
Expand Down
5 changes: 5 additions & 0 deletions src/lib/icons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ class Icon {
`;
}

static getPath(name: keyof typeof icons): string {
const path = icons[name] || '<path></path>';
return path;
}

}

export default Icon;
11 changes: 11 additions & 0 deletions src/lib/modules/menu/floating-menu/floating-menu.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<ng-container *ngFor="let toolbarItem of toolbar; let lastToolbarItem = last">
<ng-container *ngFor="let item of toolbarItem; let lastItem = last">
<div class="NgxFloatingMenu__Icon" [ngClass]="{'NgxFloatingMenu__Icon--Active': this.activeItems.includes(item),
'NgxEditor--Disabled': !this.execulableItems.includes(item)}" (mousedown)="onClick($event, item)"
*ngIf="toggleCommands.includes(item)">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" height="20" width="20"
[innerHTML]="getIcon(item)"></svg>
</div>
<div class="NgxFloatingMenu__Seperator" *ngIf="lastItem && !lastToolbarItem"></div>
</ng-container>
</ng-container>
57 changes: 57 additions & 0 deletions src/lib/modules/menu/floating-menu/floating-menu.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
:host {
position: absolute;
z-index: 20;
margin-bottom: 0.35rem;
display: flex;
border-radius: 4px;
background-color: #000;
color: white;
display: flex;
height: 1.85rem;
padding: 0.2rem 0.3rem;
visibility: hidden;
}

.NgxFloatingMenu__Icon {
height: 1.8rem;
width: 1.8rem;
transition: 0.3s ease-in-out;
border-radius: 2px;
display: flex;
align-items: center;
justify-content: center;

svg {
fill: white;
}

&:hover {
background-color: #636262;
}

+ .NgxFloatingMenu__Icon {
margin-left: 0.3rem;
}
}

.NgxFloatingMenu__Icon--Active {
background-color: white;

svg {
fill: black;
}

&:hover {
background-color: #636262;

svg {
fill: white;
}
}
}

.NgxFloatingMenu__Seperator {
border-left: 1px solid white;
height: 100%;
margin: 0 5px;
}
37 changes: 37 additions & 0 deletions src/lib/modules/menu/floating-menu/floating-menu.component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { FloatingMenuComponent } from './floating-menu.component';
import { SanitizeHtmlPipe } from '../../../pipes/sanitize/sanitize-html.pipe';
import Editor from '../../../Editor';

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

beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [
FloatingMenuComponent
],
providers: [
SanitizeHtmlPipe
]
})
.compileComponents();
});

beforeEach(() => {
fixture = TestBed.createComponent(FloatingMenuComponent);
component = fixture.componentInstance;
component.editor = new Editor();
fixture.detectChanges();
});

afterEach(() => {
component.editor.destroy();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
Loading

0 comments on commit 102ce62

Please sign in to comment.