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

[Feature] Custom floating menu #305

Closed
MateMahinKhan opened this issue Jan 4, 2021 · 16 comments
Closed

[Feature] Custom floating menu #305

MateMahinKhan opened this issue Jan 4, 2021 · 16 comments

Comments

@MateMahinKhan
Copy link
Contributor

MateMahinKhan commented Jan 4, 2021

I'm submitting a

Feature request

Description

Love the project! Thanks for your hard work.

I'm trying to implement a floating menu that would appear above the line when some text is selected

I saw the issue for menu on the bottom of the editor. I was wondering if It's possible to do it in prosemirror bypassing ngx-editor toolbar altogether?

image

Thanks for any pointers you can offer.

@sibiraj-s
Copy link
Owner

sibiraj-s commented Jan 5, 2021

I was wondering if It's possible to do it in prosemirror bypassing ngx-editor toolbar altogether?

You should be able to provide plugins to prosemirror directly via.

new Editor({
  plugins: [
    // prosemirror plugins
  ],
});

or register a plugin on the fly like.

const editor = new Editor();
editor.registerPlugin(plugin);

Refer bubble component as an example https://github.com/sibiraj-s/ngx-editor/blob/master/src/lib/components/bubble/bubble.component.ts
This is what that shows up when you click on a link in the editor. this is based on the example here https://prosemirror.net/examples/tooltip/.

Inject the component into the element with NgxEditor class (where bubble component is inserted) for proper positioning. You should be able to get a floating menu.

Plus I am working on exposing commands so that would allow to build custom menu easily.

like

const editor = new Editor();

editor
  .commands()
  .bold()
  .insertText()
  .italic()
  .exec();

but it will take few weeks. as I am occupied with personal work here.

I hope this helps.

@fredmabire
Copy link
Contributor

Hi,

I create a component for doing this.

ballon.component.html
<ngx-editor-menu class="tooltip" [editor]="editor" > </ngx-editor-menu>

ballon.component.css (see Prosemirror tooltip)

:host {
    position: absolute;
      .tooltip {
        position: absolute;
        // pointer-events: none;
        z-index: 20;
        background: white;
        border: 1px solid silver;
        border-radius: 2px;
        padding: 2px 10px;
        margin-bottom: 7px;
        -webkit-transform: translateX(-50%);
        transform: translateX(-50%);
      }

      .tooltip:before {
        content: "";
        height: 0;
        width: 0;
        position: absolute;
        left: 50%;
        margin-left: -5px;
        bottom: -6px;
        border: 5px solid transparent;
        border-bottom-width: 0;
        border-top-color: silver;
      }

      .tooltip:after {
        content: "";
        height: 0;
        width: 0;
        position: absolute;
        left: 50%;
        margin-left: -5px;
        bottom: -4.5px;
        border: 5px solid transparent;
        border-bottom-width: 0;
        border-top-color: white;
      }

}

ballon.component.ts

import {
  Component,
  ElementRef,
  Input,
  OnDestroy,
  OnInit,
  Renderer2
} from '@angular/core';
import {
  Editor
} from 'ngx-editor';
import {
  EditorView
} from 'prosemirror-view';
import {
  Subscription
} from 'rxjs';

@Component({
  selector: 'app-ballon',
  templateUrl: './ballon.component.html',
  styleUrls: ['./ballon.component.scss']
})
export class BallonComponent implements OnInit, OnDestroy {
  @Input() editor: Editor;

  private view: EditorView;
  private updateSubscription: Subscription;

  constructor(
    private el: ElementRef,
    private renderer: Renderer2
  ) {}

  private setDomPosition(): void {

    this.showBubble();
    let state = this.view.state
    let {
      from,
      to
    } = state.selection;
    let start = this.view.coordsAtPos(from),
      end = this.view.coordsAtPos(to)
 
    let box = (this.view.dom.parentNode as any).getBoundingClientRect();

    let left = (start.left + end.left) / 4;
    
    let bottom = (box.bottom - start.top + 50);
    
    this.renderer.setStyle(this.el.nativeElement, 'left', `${left}px`);
    this.renderer.setStyle(this.el.nativeElement, 'bottom', `${bottom}px`);
  }

  private showBubble(): void {
    this.renderer.setStyle(this.el.nativeElement, 'display', 'flex');
  }

  private hideBubble(): void {
    this.renderer.setStyle(this.el.nativeElement, 'display', 'none');
  }

  private update(): void {
    const {
      state
    } = this.view;

    if (state.selection.empty) {
      this.hideBubble();
      return
    }

    // update dom position
    this.setDomPosition();
  }

  ngOnInit(): void {
    this.view = this.editor.view;

    this.updateSubscription = this.editor.onUpdate.subscribe(() => {
      this.update();
    });
  }

  ngOnDestroy(): void {
    this.updateSubscription.unsubscribe();
  }

}

Add position: relative; to .editor class in app.component.scss

Then replace

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

by

<app-ballon [editor]="editor"></app-ballon> in app.component.html

Result :

image

I hope this can help you.

@sibiraj-s
Copy link
Owner

Thanks @fredmabire . It is a good one.

But I didn't add it to the lib yet since it was difficult to calculate the position of nodes with images in it. I need to try if I can do one will node_views. couldn't find time.

Also this might overlap with the builtin link bubble that showsup when selection has a link or clicked on a link

@GitHubish
Copy link

No idea if this resource can be useful to you, the Tiptap editor (usable with VueJS) also uses Prosemirror and they have a system where the toolbar appears in a bubble.

https://github.com/ueberdosis/tiptap/blob/3b8ba27cc8871a7b8949fc423a8335edf7f9c3c8/packages/tiptap/src/Components/EditorMenuBubble.js

In any case I am interested in this project. Today I'm using Quill and I'll be taking the arguments in favor of NgxEditor but that's another subject.

@sibiraj-s
Copy link
Owner

Thanks @GitHubish . I just know Tiptap, not aware of its complete features. I will look into it.

Also, I will try to add floating menu components and try to fix conflicts with link bubble that already exists.

@fredmabire
Copy link
Contributor

Hi @sibiraj-s !
Yes I saw that there was a display problem with the link bubble.
This was just a brief example for @MateMahinKhan .
I am also interested in NgxEditor and I did not know TipTap editor either (thanks @GitHubish) but some elements are interesting such as suggestions and tables.

Maybe I can help if you want.

@GitHubish
Copy link

@fredmabire cool if I could make you discover a new library.
For the @Mention feature there may be something inspired by Tiptap too and quill-mention that I use and that I had to fork for an internal project and rewritten in Typescript. Maybe I could help NgxEditor to have a module like this one.

Anyway great job @sibiraj-s

@sibiraj-s
Copy link
Owner

Thanks @GitHubish.

NgxEditor supports plugins. See https://github.com/joelewis/prosemirror-mentions.

You can do this and no changes is required from the editor side for now. there are many community provided plugins. you can use them. If nothing is available we can add it.

new Editor({
  pluigins: [
    new MentionsPlugin()
  ]
})

Create a discussion if you need any help regarding this. Let's continue it there if required. lets keep this discussion for floating menu alone.

@sibiraj-s
Copy link
Owner

I have added a bubble with minimal options as floating menu built in in v8.

It is also possible to make the current menu floating around selection with few lines of code. See https://stackblitz.com/edit/ngx-editor-floating-menu. The position calculation is not perfect yet. I will update that later.

@sibiraj-s
Copy link
Owner

I have fixed the positioning issue in demo. I will see if I could provide the same as an easy to use built in component with better positioning.

@GitHubish
Copy link

@sibiraj-s thank you it's already super cool!

The problem I often see, which would be great to fix is when the editor is in a modal and the parent has an overflow: hidden | scroll, floating toolbars are a problem if they are not created at the body level.
example : demo

Do you think it can be done?

@sibiraj-s
Copy link
Owner

Do you think it can be done?

Yes.

I have forked your example and made few changes to append to body. Checkout stackblitz

@sibiraj-s
Copy link
Owner

I just made rendering floating menu simpler in v8.0.0-beta.4

A Simple floating menu (default)

<ngx-editor [editor]="editor"> </ngx-editor>
<ngx-editor-floating-menu [editor]="editor"></ngx-editor-floating-menu> // place this whererver required.

To make the existing menu float

<ngx-editor [editor]="editor"> </ngx-editor>
<ngx-editor-floating-menu [editor]="editor">
  <ngx-editor-menu [editor]="editor"></ngx-editor-menu>
</ngx-editor-floating-menu>

You can place the floating menu anywhere in the HTML. just make sure its wrapper has postion: relative on it.

Demo: Default Floating Menu
Demo: Floating Menu with menu component

@wkulinski
Copy link

I don't know if its bug or feature but this info might be helpful for someone:
floating menu is not working when Ivy is disabled in tsconfig:

"angularCompilerOptions": { "enableIvy": false, }

You can try it here (Ivy is disabled in this example. To fix menu just enable it):
https://stackblitz.com/edit/ngx-editor-floating-menu-o7e8d1?file=tsconfig.json

@sibiraj-s
Copy link
Owner

thanks @wkulinski. Angular 9 comes with ivy enabled by default. It didn't occur to me to test in non ivy mode at the time of development. I will check this.

@github-actions
Copy link

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs and link to relevant comments in the thread.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators May 22, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants