jodit
Version:
Jodit is awesome and usefully wysiwyg editor with filebrowser
199 lines (168 loc) • 4.39 kB
text/typescript
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2020 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
import './inline-popup.less';
import './config/config';
import autobind from 'autobind-decorator';
import { Plugin } from '../../core/plugin';
import {
Buttons,
HTMLTagNames,
IBound,
IJodit,
IPopup,
IToolbarCollection,
IViewComponent,
Nullable
} from '../../types';
import { makeCollection } from '../../modules/toolbar/factory';
import { Popup } from '../../core/ui/popup';
import { splitArray, isString, position } from '../../core/helpers';
import { Dom, ToolbarCollection } from '../../modules';
import { debounce, wait } from '../../core/decorators';
/**
* Plugin for show inline popup dialog
*/
export class inlinePopup extends Plugin {
private type: Nullable<string> = null;
private popup: IPopup = new Popup(this.jodit);
private toolbar: IToolbarCollection = makeCollection(
this.jodit,
this.popup
);
private onClick(e: MouseEvent): void {
const node = e.target as Node,
elements = Object.keys(this.j.o.popup) as HTMLTagNames[],
target = Dom.isTag(node, 'img')
? node
: Dom.closest(node, elements, this.j.editor);
if (target && this.canShowPopupForType(target.nodeName.toLowerCase())) {
this.showPopup(
() => position(target, this.j),
target.nodeName.toLowerCase(),
target
);
}
}
private onSelectionChange(): void {
if (!this.j.o.toolbarInlineForSelection) {
return;
}
const type = 'selection',
sel = this.j.s.sel,
range = this.j.s.range;
if (sel?.isCollapsed) {
if (this.type === type && this.popup.isOpened) {
this.hidePopup();
}
return;
}
const node = this.j.s.current();
if (!node) {
return;
}
this.showPopup(() => range.getBoundingClientRect(), type);
}
/**
* Show inline popup with some toolbar
*
* @param rect
* @param type - selection, img, a etc.
* @param target
*/
private showPopup(
rect: () => IBound,
type: string,
target?: HTMLElement
): boolean {
type = type.toLowerCase();
if (!this.canShowPopupForType(type)) {
return false;
}
if (this.type !== type || target !== this.previousTarget) {
this.previousTarget = target;
const data = this.j.o.popup[type];
this.toolbar.buttonSize = this.j.o.toolbarButtonSize;
this.toolbar.build(data, target);
this.popup.setContent(this.toolbar.container);
this.type = type;
}
this.popup.open(rect);
return true;
}
private previousTarget?: HTMLElement;
/**
* Hide opened popup
*/
private hidePopup(): void {
this.popup.close();
}
/**
* Can show popup for this type
* @param type
*/
private canShowPopupForType(type: string): boolean {
const data = this.j.o.popup[type.toLowerCase()];
if (this.j.o.readonly || !this.j.o.toolbarInline || !data) {
return false;
}
return !this.isExcludedTarget(type);
}
/**
* For some elements do not show popup
* @param type
*/
private isExcludedTarget(type: string): boolean {
return splitArray(this.j.o.toolbarInlineDisableFor)
.map(a => a.toLowerCase())
.includes(type.toLowerCase());
}
protected afterInit(jodit: IJodit): void {
this.j.e
.on(
'getDiffButtons.mobile',
(toolbar: ToolbarCollection): void | Buttons => {
if (this.toolbar === toolbar) {
return splitArray(jodit.o.buttons).filter(item => {
const name = isString(item) ? item : item.name;
return (
name &&
name !== '|' &&
name !== '\n' &&
!this.toolbar.getButtonsNames().includes(name)
);
});
}
}
)
.on('hidePopup', this.hidePopup)
.on(
'showPopup',
(
elm: HTMLElement | string,
rect: () => IBound,
type?: string
) => {
this.showPopup(
rect,
type || (isString(elm) ? elm : elm.nodeName),
isString(elm) ? undefined : elm
);
}
)
.on('click', this.onClick)
.on(this.j.ed, 'selectionchange', this.onSelectionChange);
}
protected beforeDestruct(jodit: IJodit): void {
jodit.e
.off('showPopup')
.off('click', this.onClick)
.off(this.j.ed, 'selectionchange', this.onSelectionChange);
}
}