Browse Source

Move Droppable to TS

pull/4695/head
Artur Arseniev 4 years ago
parent
commit
c190913807
  1. 102
      src/utils/Droppable.ts

102
src/utils/Droppable.js → src/utils/Droppable.ts

@ -1,29 +1,44 @@
/*
This class makes the canvas droppable
*/
import { bindAll, indexOf } from 'underscore'; import { bindAll, indexOf } from 'underscore';
import CanvasModule from '../canvas';
import EditorModel from '../editor/model/Editor';
import { on, off } from './mixins'; import { on, off } from './mixins';
// TODO move in sorter
type SorterOptions = {
sorter: any;
event: any;
};
type DragStop = (cancel?: boolean) => void;
type DragContent = (content: any) => void;
/**
* This class makes the canvas droppable
*/
export default class Droppable { export default class Droppable {
constructor(em, rootEl) { em: EditorModel;
canvas: CanvasModule;
el: HTMLElement;
counter: number;
sortOpts?: Record<string, any> | null;
over?: boolean;
dragStop?: DragStop;
dragContent?: DragContent;
sorter?: any;
constructor(em: EditorModel, rootEl?: HTMLElement) {
this.em = em; this.em = em;
const el = this.canvas = em.get('Canvas');
rootEl || const el = rootEl || this.canvas.getFrames().map(frame => frame.getComponent().getEl());
em
.get('Canvas')
.getFrames()
.map(frame => frame.getComponent().getEl());
const els = Array.isArray(el) ? el : [el]; const els = Array.isArray(el) ? el : [el];
this.el = el; this.el = els[0];
this.counter = 0; this.counter = 0;
bindAll(this, 'handleDragEnter', 'handleDragOver', 'handleDrop', 'handleDragLeave'); bindAll(this, 'handleDragEnter', 'handleDragOver', 'handleDrop', 'handleDragLeave');
els.forEach(el => this.toggleEffects(el, 1)); els.forEach(el => this.toggleEffects(el, true));
return this;
} }
toggleEffects(el, enable) { toggleEffects(el: HTMLElement, enable: boolean) {
const methods = { on, off }; const methods = { on, off };
const method = enable ? 'on' : 'off'; const method = enable ? 'on' : 'off';
methods[method](el, 'dragenter', this.handleDragEnter); methods[method](el, 'dragenter', this.handleDragEnter);
@ -32,19 +47,19 @@ export default class Droppable {
methods[method](el, 'dragleave', this.handleDragLeave); methods[method](el, 'dragleave', this.handleDragLeave);
} }
__customTglEff(enable) { __customTglEff(enable: boolean) {
const method = enable ? on : off; const method = enable ? on : off;
const doc = this.el.ownerDocument; const doc = this.el.ownerDocument;
const frameEl = doc.defaultView.frameElement; const frameEl = doc.defaultView?.frameElement as HTMLIFrameElement;
this.sortOpts = enable this.sortOpts = enable
? { ? {
onStart({ sorter }) { onStart({ sorter }: SorterOptions) {
on(frameEl, 'pointermove', sorter.onMove); on(frameEl, 'pointermove', sorter.onMove);
}, },
onEnd({ sorter }) { onEnd({ sorter }: SorterOptions) {
off(frameEl, 'pointermove', sorter.onMove); off(frameEl, 'pointermove', sorter.onMove);
}, },
customTarget({ event }) { customTarget({ event }: SorterOptions) {
return doc.elementFromPoint(event.clientX, event.clientY); return doc.elementFromPoint(event.clientX, event.clientY);
}, },
} }
@ -63,11 +78,11 @@ export default class Droppable {
this.__customTglEff(true); this.__customTglEff(true);
} }
endCustom(cancel) { endCustom(cancel?: boolean) {
this.over ? this.endDrop(cancel) : this.__customTglEff(false); this.over ? this.endDrop(cancel) : this.__customTglEff(false);
} }
endDrop(cancel, ev) { endDrop(cancel?: boolean, ev?: Event) {
const { em, dragStop } = this; const { em, dragStop } = this;
this.counter = 0; this.counter = 0;
dragStop && dragStop(cancel); dragStop && dragStop(cancel);
@ -75,28 +90,29 @@ export default class Droppable {
em.trigger('canvas:dragend', ev); em.trigger('canvas:dragend', ev);
} }
handleDragLeave(ev) { handleDragLeave(ev: Event) {
this.updateCounter(-1, ev); this.updateCounter(-1, ev);
} }
updateCounter(value, ev) { updateCounter(value: number, ev: Event) {
this.counter += value; this.counter += value;
this.counter === 0 && this.endDrop(1, ev); this.counter === 0 && this.endDrop(true, ev);
} }
handleDragEnter(ev) { handleDragEnter(ev: DragEvent | Event) {
const { em } = this; const { em } = this;
const dt = ev.dataTransfer; const dt = (ev as DragEvent).dataTransfer;
this.updateCounter(1, ev); this.updateCounter(1, ev);
if (this.over) return; if (this.over) return;
this.over = 1; this.over = true;
const utils = em.get('Utils'); const utils = em.get('Utils');
const canvas = em.get('Canvas'); const canvas = em.get('Canvas');
// For security reason I can't read the drag data on dragenter, but // For security reason I can't read the drag data on dragenter, but
// as I need it for the Sorter context I will use `dragContent` or just // as I need it for the Sorter context I will use `dragContent` or just
// any not empty element // any not empty element
let content = em.get('dragContent') || '<br>'; let content = em.get('dragContent') || '<br>';
let dragStop, dragContent; let dragStop: DragStop;
let dragContent;
em.stopDefault(); em.stopDefault();
// Select the right drag provider // Select the right drag provider
@ -108,7 +124,7 @@ export default class Droppable {
guidesInfo: 1, guidesInfo: 1,
center: 1, center: 1,
target, target,
onEnd: (ev, dragger, { cancelled }) => { onEnd: (ev: any, dragger: any, { cancelled }: any) => {
let comp; let comp;
if (!cancelled) { if (!cancelled) {
comp = wrapper.append(content)[0]; comp = wrapper.append(content)[0];
@ -124,8 +140,8 @@ export default class Droppable {
target.remove(); target.remove();
}, },
}); });
dragStop = cancel => dragger.stop(ev, { cancel }); dragStop = (cancel?: boolean) => dragger.stop(ev, { cancel });
dragContent = cnt => (content = cnt); dragContent = (cnt: any) => (content = cnt);
} else { } else {
const sorter = new utils.Sorter({ const sorter = new utils.Sorter({
em, em,
@ -138,18 +154,18 @@ export default class Droppable {
containerSel: '*', containerSel: '*',
itemSel: '*', itemSel: '*',
pfx: 'gjs-', pfx: 'gjs-',
onEndMove: model => this.handleDragEnd(model, dt), onEndMove: (model: any) => this.handleDragEnd(model, dt),
document: this.el.ownerDocument, document: this.el.ownerDocument,
...(this.sortOpts || {}), ...(this.sortOpts || {}),
}); });
sorter.setDropContent(content); sorter.setDropContent(content);
sorter.startSort(); sorter.startSort();
this.sorter = sorter; this.sorter = sorter;
dragStop = cancel => { dragStop = (cancel?: boolean) => {
cancel && (sorter.moved = 0); cancel && (sorter.moved = 0);
sorter.endMove(); sorter.endMove();
}; };
dragContent = content => sorter.setDropContent(content); dragContent = (content: any) => sorter.setDropContent(content);
} }
this.dragStop = dragStop; this.dragStop = dragStop;
@ -157,9 +173,9 @@ export default class Droppable {
em.trigger('canvas:dragenter', dt, content); em.trigger('canvas:dragenter', dt, content);
} }
handleDragEnd(model, dt) { handleDragEnd(model: any, dt: any) {
const { em } = this; const { em } = this;
this.over = 0; this.over = false;
if (model) { if (model) {
em.set('dragResult', model); em.set('dragResult', model);
em.trigger('canvas:drop', dt, model); em.trigger('canvas:drop', dt, model);
@ -171,7 +187,7 @@ export default class Droppable {
* Always need to have this handler active for enabling the drop * Always need to have this handler active for enabling the drop
* @param {Event} ev * @param {Event} ev
*/ */
handleDragOver(ev) { handleDragOver(ev: Event) {
ev.preventDefault(); ev.preventDefault();
this.em.trigger('canvas:dragover', ev); this.em.trigger('canvas:dragover', ev);
} }
@ -180,17 +196,17 @@ export default class Droppable {
* WARNING: This function might fail to run on drop, for example, when the * WARNING: This function might fail to run on drop, for example, when the
* drop, accidentally, happens on some external element (DOM not inside the iframe) * drop, accidentally, happens on some external element (DOM not inside the iframe)
*/ */
handleDrop(ev) { handleDrop(ev: Event | DragEvent) {
ev.preventDefault(); ev.preventDefault();
const { dragContent } = this; const { dragContent } = this;
const dt = ev.dataTransfer; const dt = (ev as DragEvent).dataTransfer;
const content = this.getContentByData(dt).content; const content = this.getContentByData(dt).content;
ev.target.style.border = ''; (ev.target as HTMLElement).style.border = '';
content && dragContent && dragContent(content); content && dragContent && dragContent(content);
this.endDrop(!content, ev); this.endDrop(!content, ev);
} }
getContentByData(dt) { getContentByData(dt: any) {
const em = this.em; const em = this.em;
const types = dt && dt.types; const types = dt && dt.types;
const files = (dt && dt.files) || []; const files = (dt && dt.files) || [];
Loading…
Cancel
Save