diff --git a/src/abstract/DomainViews.ts b/src/abstract/DomainViews.ts new file mode 100644 index 000000000..0750cd2fc --- /dev/null +++ b/src/abstract/DomainViews.ts @@ -0,0 +1,102 @@ +import { includes } from 'underscore'; +import Backbone from 'backbone'; +import View from './View'; +import Model from './Model'; +/*interface DomainView{ + constructor(model: TModel): TView +}*/ +type TModel = TCollection extends Backbone.Collection? TModel: Model; + +export default abstract class DomainViews, TItemView extends View> extends View> { + // Defines the View per type + itemsView = ''; + + protected itemType = 'type'; + + reuseView = false; + + viewCollection: TItemView[] = []; + constructor(opts: any = {}, autoAdd = false) { + super(opts); + autoAdd && this.listenTo(this.collection, 'add', this.addTo); + } + + /** + * Add new model to the collection + * @param {Model} model + * @private + * */ + private addTo(model: TModel) { + this.add(model); + } + + private itemViewNotFound(type: string) { + /*const { em, ns } = this; + const warn = `${ns ? `[${ns}]: ` : ''}'${type}' type not found`; + em?.logWarning(warn);*/ + } + protected abstract renderView(model: TModel, itemType: string): TItemView; + + /** + * Render new model inside the view + * @param {Model} model + * @param {Object} fragment Fragment collection + * @private + * */ + private add(model: TModel, fragment?: DocumentFragment) { + const { config, reuseView, viewCollection, itemsView = {} } = this; + var frag = fragment || null; + var typeField = model.get(this.itemType); + let view; + + //@ts-ignore + if (model.view && reuseView) { + //@ts-ignore + view = model.view; + } else { + view = this.renderView(model, typeField); + } + + viewCollection.push(view); + const rendered = view.render().el; + + if (frag) frag.appendChild(rendered); + else this.$el.append(rendered); + } + + render() { + var frag = document.createDocumentFragment(); + this.clearItems(); + this.$el.empty(); + + if (this.collection.length) + this.collection.each((model) => { + this.add(model, frag); + }, this); + + this.$el.append(frag); + this.onRender(); + return this; + } + + onRender() {} + + onRemoveBefore(items: TItemView[], opts: any) {} + onRemove(items: TItemView[], opts: any) {} + + remove(opts: any = {}) { + const { viewCollection } = this; + this.onRemoveBefore(viewCollection, opts); + this.clearItems(); + Backbone.View.prototype.remove.apply(this, opts); + this.onRemove(viewCollection, opts); + return this; + } + + clearItems() { + const items = this.viewCollection || []; + // TODO Traits do not update the target anymore + // items.forEach(item => item.remove()); + // this.items = []; + } +} diff --git a/src/abstract/Model.ts b/src/abstract/Model.ts index cc7b498ad..324e27036 100644 --- a/src/abstract/Model.ts +++ b/src/abstract/Model.ts @@ -26,10 +26,6 @@ export default class Model< return this._module.config; } - protected get em() { - return this._module.em; - } - public get em() { return this._module.em; } diff --git a/src/abstract/View.ts b/src/abstract/View.ts index a52d6ee93..48034ad05 100644 --- a/src/abstract/View.ts +++ b/src/abstract/View.ts @@ -7,7 +7,7 @@ export default class View< TElement extends Element = HTMLElement > extends Backbone.View { protected get pfx() { - return (this.model.em.config as any).stylePrefix || ""; + return (this.em.config as any).stylePrefix || ""; } protected get ppfx() { @@ -15,11 +15,12 @@ export default class View< } protected get module(): TModel extends Model? M: unknown { - return this.model.module as any; + //console.log((this.collection.first as any).module) + return this.model?.module ?? (this.collection as any).module; } protected get em() { - return this.model.em; + return this.module.em; } protected get config(): TModel extends Model ? (M extends IBaseModule ? C : unknown) : unknown{ diff --git a/src/canvas/index.ts b/src/canvas/index.ts index 22f087e41..841e8b595 100644 --- a/src/canvas/index.ts +++ b/src/canvas/index.ts @@ -69,16 +69,10 @@ export default class CanvasModule extends Module { } //name = 'Canvas'; - - c: any; - //em: EditorModel; canvas: Canvas; model: Canvas; - private _canvasView?: CanvasView; + private canvasView?: CanvasView; - get canvasView(): CanvasView{ - return this._canvasView as any - } /** * Initialize module. Automatically called with a new instance of the editor * @param {Object} config Configurations @@ -86,10 +80,7 @@ export default class CanvasModule extends Module { */ constructor(em: EditorModel) { super(em, "Canvas", defaults) - this.c = { - ...this.config, - module: this, - }; + this.canvas = new Canvas(this); this.model = this.canvas; this.startAutoscroll = this.startAutoscroll.bind(this); @@ -108,22 +99,12 @@ export default class CanvasModule extends Module { return this.canvas; } - /** - * Get the configuration object - * @returns {Object} Configuration object - * @example - * console.log(canvas.getConfig()) - */ - getConfig() { - return this.c; - } - /** * Get the canvas element * @returns {HTMLElement} */ getElement() { - return this.canvasView.el; + return this.getCanvasView().el; } getFrame(index?: number) { @@ -134,9 +115,9 @@ export default class CanvasModule extends Module { * Get the main frame element of the canvas * @returns {HTMLIFrameElement} */ - getFrameEl(): HTMLIFrameElement { + getFrameEl() { const { frame } = this.canvasView || {}; - return frame && frame.el; + return frame?.el as HTMLIFrameElement; } getFramesEl() { @@ -148,7 +129,8 @@ export default class CanvasModule extends Module { * @returns {Window} */ getWindow() { - return this.getFrameEl().contentWindow; + const { frame } = this.canvasView || {}; + return frame?.getWindow() as Window; } /** @@ -157,7 +139,7 @@ export default class CanvasModule extends Module { */ getDocument() { const frame = this.getFrameEl(); - return frame && frame.contentDocument as Document; + return frame?.contentDocument as Document; } /** @@ -166,7 +148,7 @@ export default class CanvasModule extends Module { */ getBody() { const doc = this.getDocument(); - return doc && doc.body; + return doc?.body as HTMLBodyElement; } _getLocalEl(globalEl: any, compView: any, method: keyof FrameView) { @@ -192,7 +174,7 @@ export default class CanvasModule extends Module { * @private */ getToolsEl(compView: any) { - return this._getLocalEl(this.canvasView.toolsEl, compView, 'getToolsEl'); + return this._getLocalEl(this.getCanvasView().toolsEl, compView, 'getToolsEl'); } /** @@ -201,7 +183,7 @@ export default class CanvasModule extends Module { * @private */ getHighlighter(compView: any) { - return this._getLocalEl(this.canvasView.hlEl, compView, 'getHighlighter'); + return this._getLocalEl(this.getCanvasView().hlEl, compView, 'getHighlighter'); } /** @@ -210,7 +192,7 @@ export default class CanvasModule extends Module { * @private */ getBadgeEl(compView: any) { - return this._getLocalEl(this.canvasView.badgeEl, compView, 'getBadgeEl'); + return this._getLocalEl(this.getCanvasView().badgeEl, compView, 'getBadgeEl'); } /** @@ -219,7 +201,7 @@ export default class CanvasModule extends Module { * @private */ getPlacerEl() { - return this.canvasView.placerEl; + return this.getCanvasView().placerEl; } /** @@ -228,7 +210,7 @@ export default class CanvasModule extends Module { * @private */ getGhostEl() { - return this.canvasView.ghostEl; + return this.getCanvasView().ghostEl; } /** @@ -237,7 +219,7 @@ export default class CanvasModule extends Module { * @private */ getToolbarEl() { - return this.canvasView.toolbarEl; + return this.getCanvasView().toolbarEl; } /** @@ -246,7 +228,7 @@ export default class CanvasModule extends Module { * @private */ getResizerEl() { - return this.canvasView.resizerEl; + return this.getCanvasView().resizerEl; } /** @@ -255,7 +237,7 @@ export default class CanvasModule extends Module { * @private */ getOffsetViewerEl(compView: any) { - return this._getLocalEl(this.canvasView.offsetEl, compView, 'getOffsetViewerEl'); + return this._getLocalEl(this.getCanvasView().offsetEl, compView, 'getOffsetViewerEl'); } /** @@ -264,12 +246,12 @@ export default class CanvasModule extends Module { * @private */ getFixedOffsetViewerEl() { - return this.canvasView.fixedOffsetEl; + return this.getCanvasView().fixedOffsetEl; } render() { this.canvasView?.remove(); - this._canvasView = new CanvasView({model: this.canvas, config: this.c}); + this.canvasView = new CanvasView(this.canvas); return this.canvasView.render().el; } @@ -294,7 +276,7 @@ export default class CanvasModule extends Module { * @private */ offset(el: HTMLElement) { - return this.canvasView?.offset(el); + return this.getCanvasView().offset(el); } /** @@ -306,7 +288,8 @@ export default class CanvasModule extends Module { * }); */ setCustomBadgeLabel(f: Function) { - this.c.customBadgeLabel = f; + //@ts-ignore + this.config.customBadgeLabel = f; } /** @@ -316,7 +299,7 @@ export default class CanvasModule extends Module { * @private */ getElementPos(el: HTMLElement, opts?: any) { - return this.canvasView.getElementPos(el, opts); + return this.getCanvasView().getElementPos(el, opts); } /** @@ -326,7 +309,7 @@ export default class CanvasModule extends Module { * @private */ getElementOffsets(el: HTMLElement) { - return this.canvasView.getElementOffsets(el); + return this.getCanvasView().getElementOffsets(el); } /** @@ -334,9 +317,9 @@ export default class CanvasModule extends Module { * @returns {Object} */ getRect() { - const { top, left } = this.canvasView.getPosition(); + const { top = 0, left = 0 } = this.getCanvasView().getPosition() ?? {}; return { - ...this.canvasView.getCanvasOffset(), + ...this.getCanvasView().getCanvasOffset(), topScroll: top, leftScroll: left, }; @@ -360,9 +343,9 @@ export default class CanvasModule extends Module { */ getTargetToElementDim(target: HTMLElement, element: HTMLElement, options: any = {}) { var opts = options || {}; - var canvasPos = this.canvasView.getPosition(); + var canvasPos = this.getCanvasView().getPosition(); if (!canvasPos) return; - var pos = opts.elPos || this.canvasView.getElementPos(element); + var pos = opts.elPos || this.getCanvasView().getElementPos(element); var toRight = options.toRight || 0; var targetHeight = opts.targetHeight || target.offsetHeight; var targetWidth = opts.targetWidth || target.offsetWidth; @@ -442,7 +425,8 @@ export default class CanvasModule extends Module { let top = -toolbarH; let left = !isUndefined(opts.left) ? opts.left : pos.width - toolbarW; left = pos.left < -left ? -pos.left : left; - left = elRight > frCvOff.width ? left - (elRight - frCvOff.width) : left; + const frCvWidth = frCvOff?.width ?? 0; + left = elRight > frCvWidth ? left - (elRight - frCvWidth) : left; // Scroll with the window if the top edge is reached and the // element is bigger than the canvas @@ -509,7 +493,7 @@ export default class CanvasModule extends Module { */ getMouseRelativeCanvas(ev: MouseEvent, opts: any) { const zoom = this.getZoomDecimal(); - const { top, left } = this.getCanvasView().getPosition(opts); + const { top = 0, left = 0 } = this.getCanvasView().getPosition(opts) ?? {}; return { y: ev.clientY * zoom + top, @@ -670,13 +654,13 @@ export default class CanvasModule extends Module { * }); */ addFrame(props = {}, opts = {}) { - return this.canvas.frames.add(new Frame({ ...props }, { em: this.em }), opts); + return this.canvas.frames.add(new Frame(this, { ...props }), opts); } destroy() { this.canvas.stopListening(); this.canvasView?.remove(); - [this.c, this.canvas, this.canvasView].forEach(i => (i = {})); + //[this.canvas, this.canvasView].forEach(i => (i = {})); //@ts-ignore ['model', 'droppable'].forEach(i => (this[i] = {})); } diff --git a/src/canvas/model/Canvas.ts b/src/canvas/model/Canvas.ts index 31b84b7df..5b24e5c64 100644 --- a/src/canvas/model/Canvas.ts +++ b/src/canvas/model/Canvas.ts @@ -8,7 +8,7 @@ export default class Canvas extends Model { defaults() { return { frame: '', - frames: new Frames(), + frames: [], rulers: false, zoom: 100, x: 0, @@ -24,6 +24,7 @@ export default class Canvas extends Model { const { em, config } = module; const { scripts, styles } = config; super(module, {scripts, styles}); + this.set("frames", new Frames(module)) this.listenTo(this, "change:zoom", this.onZoomChange); this.listenTo(em, "change:device", this.updateDevice); this.listenTo(em, evPageSelect, this._pageUpdated); diff --git a/src/canvas/model/Frame.ts b/src/canvas/model/Frame.ts index d21ba29b5..adc6740bb 100644 --- a/src/canvas/model/Frame.ts +++ b/src/canvas/model/Frame.ts @@ -1,15 +1,16 @@ -import { result, forEach, isEmpty, isString } from 'underscore'; -import { Model } from '../../common'; -import { Component } from '../../dom_components/model/Component'; -import Components from '../../dom_components/model/Components'; -import ComponentWrapper from '../../dom_components/model/ComponentWrapper'; -import EditorModel from '../../editor/model/Editor'; -import { isComponent, isObject } from '../../utils/mixins'; -import FrameView from '../view/FrameView'; -import Frames from './Frames'; - -const keyAutoW = '__aw'; -const keyAutoH = '__ah'; +import { result, forEach, isEmpty, isString } from "underscore"; +import CanvasModule from ".."; +import { Model } from "../../abstract"; +import Component from "../../dom_components/model/Component"; +import Components from "../../dom_components/model/Components"; +import ComponentWrapper from "../../dom_components/model/ComponentWrapper"; +import EditorModel from "../../editor/model/Editor"; +import { isComponent, isObject } from "../../utils/mixins"; +import FrameView from "../view/FrameView"; +import Frames from "./Frames"; + +const keyAutoW = "__aw"; +const keyAutoH = "__ah"; /** * @property {Object|String} component Wrapper component definition. You can also pass an HTML string as components of the default wrapper component. @@ -19,7 +20,7 @@ const keyAutoH = '__ah'; * @property {Number} [y=0] Vertical position of the frame in the canvas. * */ -export default class Frame extends Model { +export default class Frame extends Model { defaults() { return { x: 0, @@ -35,18 +36,16 @@ export default class Frame extends Model { _undoexc: ['changesCount'], }; } - em: EditorModel; view?: FrameView; - constructor(props: any, opts: any) { - super(props); - const { em } = opts; + constructor(module: CanvasModule, props: any) { + super(module, props); + const { em } = this; const { styles, component } = this.attributes; const domc = em.get('DomComponents'); const conf = domc.getConfig(); const allRules = em.get('CssComposer').getAll(); const idMap: any = {}; - this.em = em; const modOpts = { em, config: conf, frame: this, idMap }; if (!isComponent(component)) { @@ -87,6 +86,10 @@ export default class Frame extends Model { !props.height && this.set(keyAutoH, 1); } + get head(): {tag: string, attributes: any}[]{ + return this.get("head"); + } + onRemove() { this.getComponent().remove({ root: 1 }); } @@ -117,23 +120,19 @@ export default class Frame extends Model { } getHead() { - const head = this.get('head') || []; - return [...head]; + return [...this.head]; } - setHead(value: any) { - return this.set('head', [...value]); + setHead(value: {tag: string, attributes: any}[]) { + return this.set("head", [...value]); } - addHeadItem(item: any) { - const head = this.getHead(); - head.push(item); - this.setHead(head); + addHeadItem(item: {tag: string, attributes: any}) { + this.head.push(item); } getHeadByAttr(attr: string, value: any, tag: string) { - const head = this.getHead(); - return head.filter( + return this.head.filter( (item) => item.attributes && item.attributes[attr] == value && @@ -142,13 +141,11 @@ export default class Frame extends Model { } removeHeadByAttr(attr: string, value: any, tag: string) { - const head = this.getHead(); const item = this.getHeadByAttr(attr, value, tag); - const index = head.indexOf(item); + const index = this.head.indexOf(item); if (index >= 0) { - head.splice(index, 1); - this.setHead(head); + this.head.splice(index, 1); } } diff --git a/src/canvas/model/Frames.ts b/src/canvas/model/Frames.ts index 2db54e0f9..e69f3ca28 100644 --- a/src/canvas/model/Frames.ts +++ b/src/canvas/model/Frames.ts @@ -1,4 +1,5 @@ import { bindAll } from 'underscore'; +import CanvasModule from '..'; import { Collection } from '../../common'; import Page from '../../pages/model/Page'; import Frame from './Frame'; @@ -7,9 +8,11 @@ export default class Frames extends Collection { loadedItems = 0; itemsToLoad = 0; page?: Page; + module: CanvasModule - constructor(models?: Frame[]) { + constructor(module: CanvasModule, models: Frame[] = []) { super(models); + this.module = module; bindAll(this, 'itemLoaded'); this.on('reset', this.onReset); this.on('remove', this.onRemove); diff --git a/src/canvas/view/CanvasView.ts b/src/canvas/view/CanvasView.ts index 7994e0404..e8a161a2e 100644 --- a/src/canvas/view/CanvasView.ts +++ b/src/canvas/view/CanvasView.ts @@ -4,9 +4,7 @@ import { on, off, getElement, getKeyChar, isTextNode, getElRect, getUiClass } fr import { createEl } from '../../utils/dom'; import FramesView from './FramesView'; import Canvas from '../model/Canvas'; -import Frame from '../model/Frame'; import FrameView from './FrameView'; -import Components from '../../dom_components/model/Components'; import ComponentView from '../../dom_components/view/ComponentView'; import Component from '../../dom_components/model/Component'; @@ -79,13 +77,13 @@ export default class CanvasView extends View { _initFrames() { const { frames, model, config, em } = this; - const collection = model.get('frames'); + const collection = model.frames; em.set('readyCanvas', 0); collection.once('loaded:all', () => em.set('readyCanvas', 1)); frames?.remove(); - this.frames = new FramesView({ - collection, - config: { + this.frames = new FramesView( + {collection}, + {config: { ...config, canvasView: this, }, @@ -296,7 +294,7 @@ export default class CanvasView extends View { * @public */ getPosition(opts: any = {}) { - + const doc = this.frame?.el.contentDocument; if (!doc) return; const bEl = doc.body; diff --git a/src/canvas/view/FrameView.js b/src/canvas/view/FrameView.ts similarity index 79% rename from src/canvas/view/FrameView.js rename to src/canvas/view/FrameView.ts index 0fd2d08b8..fa4a2c15c 100644 --- a/src/canvas/view/FrameView.js +++ b/src/canvas/view/FrameView.ts @@ -1,31 +1,47 @@ import { bindAll, isString, debounce, isUndefined } from 'underscore'; import { appendVNodes, append, createEl, createCustomEvent, motionsEv } from '../../utils/dom'; import { on, off, setViewEl, hasDnd, getPointerEvent } from '../../utils/mixins'; -import { View } from '../../common'; +import { View } from '../../abstract'; import CssRulesView from '../../css_composer/view/CssRulesView'; import Droppable from '../../utils/Droppable'; +import Frame from '../model/Frame'; +import Canvas from '../model/Canvas'; +import ComponentWrapper from '../../dom_components/model/ComponentWrapper'; +import FrameWrapView from './FrameWrapView'; -export default class FrameView extends View { - tagName() { - return 'iframe'; - } +export default class FrameView extends View { - attributes() { - return { - allowfullscreen: 'allowfullscreen', - }; - } + //@ts-ignore + get tagName(){return 'iframe'}; + //@ts-ignore + get attributes() {return { allowfullscreen: 'allowfullscreen' }}; + + dragging = false; + droppable?: Droppable; + rect?: DOMRect; - initialize(o) { + lastClientY?: number; + lastMaxHeight = 0; + private jsContainer?: HTMLElement; + private tools: {[key: string]: HTMLElement} = {}; + private wrapper?: any; + private frameWrapView?: FrameWrapView; + + + constructor(model: Frame, view?: FrameWrapView) { + super({model}); bindAll(this, 'updateClientY', 'stopAutoscroll', 'autoscroll', '_emitUpdate'); - const { model, el } = this; - this.tools = {}; - this.config = { - ...(o.config || {}), + const { el, em } = this; + //el = em.config.el + //@ts-ignore + this.module._config = { + ...(this.config || {}), + //@ts-ignore frameView: this, + //canvasView: view?.cv }; - this.ppfx = this.config.pStylePrefix || ''; - this.em = this.model.em; + //console.log(this.config) + this.frameWrapView = view; this.showGlobalTools = debounce(this.showGlobalTools.bind(this), 50); const cvModel = this.getCanvasModel(); this.listenTo(model, 'change:head', this.updateHead); @@ -40,16 +56,16 @@ export default class FrameView extends View { updateHead() { const { model } = this; const headEl = this.getHead(); - const toRemove = []; - const toAdd = []; - const current = model.get('head'); + const toRemove: any[] = []; + const toAdd: any[] = []; + const current = model.head; const prev = model.previous('head'); - const attrStr = (attr = {}) => + const attrStr = (attr: any = {}) => Object.keys(attr) .sort() .map(i => `[${i}="${attr[i]}"]`) .join(''); - const find = (items, stack, res) => { + const find = (items: any[], stack: any[], res: any[]) => { items.forEach(item => { const { tag, attributes } = item; const has = stack.some(s => s.tag === tag && attrStr(s.attributes) === attrStr(attributes)); @@ -60,7 +76,7 @@ export default class FrameView extends View { find(prev, current, toRemove); toRemove.forEach(stl => { const el = headEl.querySelector(`${stl.tag}${attrStr(stl.attributes)}`); - el && el.parentNode.removeChild(el); + el?.parentNode?.removeChild(el); }); appendVNodes(headEl, toAdd); } @@ -69,28 +85,28 @@ export default class FrameView extends View { return this.el; } - getCanvasModel() { + getCanvasModel(): Canvas { return this.em.get('Canvas').getModel(); } getWindow() { - return this.getEl().contentWindow; + return this.getEl().contentWindow as Window; } getDoc() { - return this.getEl().contentDocument; + return this.getEl().contentDocument as Document; } getHead() { - return this.getDoc().querySelector('head'); + return this.getDoc().querySelector('head') as HTMLHeadElement; } getBody() { - return this.getDoc().querySelector('body'); + return this.getDoc().querySelector('body') as HTMLBodyElement; } getWrapper() { - return this.getBody().querySelector('[data-gjs-type=wrapper]'); + return this.getBody().querySelector('[data-gjs-type=wrapper]') as HTMLElement; } getJsContainer() { @@ -102,8 +118,7 @@ export default class FrameView extends View { } getToolsEl() { - const { frameWrapView } = this.config; - return frameWrapView && frameWrapView.elTools; + return this.frameWrapView?.elTools as HTMLElement; } getGlobalToolsEl() { @@ -151,23 +166,24 @@ export default class FrameView extends View { }; } - _getTool(name) { + _getTool(name: string) { const { tools } = this; const toolsEl = this.getToolsEl(); if (!tools[name]) { - tools[name] = toolsEl.querySelector(name); + tools[name] = toolsEl.querySelector(name) as HTMLElement; } return tools[name]; } - remove() { + remove(...args: any) { const wrp = this.wrapper; - this._toggleEffects(); + this._toggleEffects(false); this.tools = {}; wrp && wrp.remove(); - View.prototype.remove.apply(this, arguments); + View.prototype.remove.apply(this, args); + return this; } startAutoscroll() { @@ -176,7 +192,7 @@ export default class FrameView extends View { // By detaching those from the stack avoid browsers lags // Noticeable with "fast" drag of blocks setTimeout(() => { - this._toggleAutoscrollFx(1); + this._toggleAutoscrollFx(true); requestAnimationFrame(this.autoscroll); }, 0); } @@ -217,7 +233,7 @@ export default class FrameView extends View { } } - updateClientY(ev) { + updateClientY(ev: Event) { ev.preventDefault(); this.lastClientY = getPointerEvent(ev).clientY * this.em.getZoomDecimal(); } @@ -227,10 +243,10 @@ export default class FrameView extends View { } stopAutoscroll() { - this.dragging && this._toggleAutoscrollFx(); + this.dragging && this._toggleAutoscrollFx(false); } - _toggleAutoscrollFx(enable) { + _toggleAutoscrollFx(enable: boolean) { this.dragging = enable; const win = this.getWindow(); const method = enable ? 'on' : 'off'; @@ -251,7 +267,7 @@ export default class FrameView extends View { const evLoad = 'frame:load'; const evOpts = { el, model, view: this }; const canvas = this.getCanvasModel(); - const appendScript = scripts => { + const appendScript = (scripts: any[]) => { if (scripts.length > 0) { const src = scripts.shift(); const scriptEl = createEl('script', { @@ -259,7 +275,7 @@ export default class FrameView extends View { ...(isString(src) ? { src } : src), }); scriptEl.onerror = scriptEl.onload = appendScript.bind(null, scripts); - el.contentDocument.head.appendChild(scriptEl); + el.contentDocument?.head.appendChild(scriptEl); } else { this.renderBody(); em && em.trigger(evLoad, evOpts); @@ -279,10 +295,10 @@ export default class FrameView extends View { }; } - renderStyles(opts = {}) { + renderStyles(opts: any = {}) { const head = this.getHead(); const canvas = this.getCanvasModel(); - const normalize = stls => + const normalize = (stls: any[]) => stls.map(href => ({ tag: 'link', attributes: { @@ -292,9 +308,9 @@ export default class FrameView extends View { })); const prevStyles = normalize(opts.prev || canvas.previous('styles')); const styles = normalize(canvas.get('styles')); - const toRemove = []; - const toAdd = []; - const find = (items, stack, res) => { + const toRemove: any[] = []; + const toAdd: any[] = []; + const find = (items: any[], stack: any[], res: any[]) => { items.forEach(item => { const { href } = item.attributes; const has = stack.some(s => s.attributes.href === href); @@ -305,7 +321,7 @@ export default class FrameView extends View { find(prevStyles, styles, toRemove); toRemove.forEach(stl => { const el = head.querySelector(`link[href="${stl.attributes.href}"]`); - el && el.parentNode.removeChild(el); + el?.parentNode?.removeChild(el); }); appendVNodes(head, toAdd); } @@ -316,6 +332,7 @@ export default class FrameView extends View { const body = this.getBody(); const win = this.getWindow(); const conf = em.config; + //@ts-ignore TODO I don't understand why this needed nowhere else is used win._isEditor = true; this.renderStyles({ prev: [] }); @@ -398,11 +415,12 @@ export default class FrameView extends View { frameView: this, }, }).render(); - append(body, this.wrapper.el); + append(body, this.wrapper?.el); append( body, new CssRulesView({ collection: model.getStyles(), + //@ts-ignore config: { ...em.get('CssComposer').getConfig(), frameView: this, @@ -414,7 +432,8 @@ export default class FrameView extends View { //this.updateOffset(); // TOFIX (check if I need it) // Avoid some default behaviours - on(body, 'click', ev => ev && ev.target.tagName == 'A' && ev.preventDefault()); + //@ts-ignore + on(body, 'click', ev => ev && ev.target?.tagName == 'A' && ev.preventDefault()); on(body, 'submit', ev => ev && ev.preventDefault()); // When the iframe is focused the event dispatcher is not the same so @@ -430,12 +449,12 @@ export default class FrameView extends View { }) ); - this._toggleEffects(1); - this.droppable = hasDnd(em) && new Droppable(em, this.wrapper.el); + this._toggleEffects(true); + this.droppable = hasDnd(em) && new Droppable(em, this.wrapper?.el); model.trigger('loaded'); } - _toggleEffects(enable) { + _toggleEffects(enable: boolean) { const method = enable ? on : off; const win = this.getWindow(); win && method(win, `${motionsEv} resize`, this._emitUpdate); diff --git a/src/canvas/view/FrameWrapView.js b/src/canvas/view/FrameWrapView.ts similarity index 83% rename from src/canvas/view/FrameWrapView.js rename to src/canvas/view/FrameWrapView.ts index 0bf752f8b..89f36d2ca 100644 --- a/src/canvas/view/FrameWrapView.js +++ b/src/canvas/view/FrameWrapView.ts @@ -1,34 +1,37 @@ import { bindAll, isNumber, isNull, debounce } from 'underscore'; -import { View } from '../../common'; +import { View } from '../../abstract'; import FrameView from './FrameView'; import { createEl, removeEl } from '../../utils/dom'; import Dragger from '../../utils/Dragger'; +import CanvasView from './CanvasView'; +import Frame from '../model/Frame'; -export default class FrameWrapView extends View { +export default class FrameWrapView extends View { events() { return { 'click [data-action-remove]': 'remove', 'mousedown [data-action-move]': 'startDrag', }; } - - initialize(opts = {}, conf = {}) { + elTools?: HTMLElement; + frame: FrameView; + dragger?: Dragger; + cv: CanvasView + classAnim: string + + constructor(model: Frame, canvasView: CanvasView) { + super({model}); bindAll(this, 'onScroll', 'frameLoaded', 'updateOffset', 'remove', 'startDrag'); - const { model } = this; + //console.log(model.module) const config = { - ...(opts.config || conf), + ...(model.config), frameWrapView: this, }; - const { canvasView } = config; this.cv = canvasView; - this.config = config; - this.em = this.model.em; - this.canvas = this.em?.get('Canvas'); - this.ppfx = config.pStylePrefix || ''; - this.frame = new FrameView({ model, config }); + this.frame = new FrameView(model, this); this.classAnim = `${this.ppfx}frame-wrapper--anim`; - this.updateOffset = debounce(this.updateOffset.bind(this)); - this.updateSize = debounce(this.updateSize.bind(this)); + this.updateOffset = debounce(this.updateOffset.bind(this), 0); + this.updateSize = debounce(this.updateSize.bind(this), 0); this.listenTo(model, 'loaded', this.frameLoaded); this.listenTo(model, 'change:x change:y', this.updatePos); this.listenTo(model, 'change:width change:height', this.updateSize); @@ -38,10 +41,10 @@ export default class FrameWrapView extends View { } setupDragger() { - const { canvas, model } = this; - let dragX, dragY, zoom; - const toggleEffects = on => { - canvas.toggleFramesEvents(on); + const { module, model } = this; + let dragX: number, dragY: number, zoom: number; + const toggleEffects = (on: boolean) => { + module.toggleFramesEvents(on); }; this.dragger = new Dragger({ @@ -50,10 +53,10 @@ export default class FrameWrapView extends View { zoom = this.em.getZoomMultiplier(); dragX = x; dragY = y; - toggleEffects(); + toggleEffects(false); }, - onEnd: () => toggleEffects(1), - setPosition: posOpts => { + onEnd: () => toggleEffects(true), + setPosition: (posOpts: any) => { model.set({ x: dragX + posOpts.x * zoom, y: dragY + posOpts.y * zoom, @@ -62,20 +65,21 @@ export default class FrameWrapView extends View { }); } - startDrag(ev) { - ev && this.dragger.start(ev); + startDrag(ev?: Event) { + ev && this.dragger?.start(ev); } - __clear(opts) { + __clear(opts?: any) { const { frame } = this; frame && frame.remove(opts); removeEl(this.elTools); } - remove(opts) { + remove(opts?: any) { this.__clear(opts); - View.prototype.remove.apply(this, arguments); - ['frame', 'dragger', 'cv', 'em', 'canvas', 'elTools'].forEach(i => (this[i] = 0)); + View.prototype.remove.apply(this, opts); + //@ts-ignore + ['frame', 'dragger', 'cv', 'elTools'].forEach(i => (this[i] = 0)); return this; } @@ -87,11 +91,11 @@ export default class FrameWrapView extends View { frame.model._emitUpdated(); } - updatePos(md) { + updatePos(md?: boolean) { const { model, el } = this; const { x, y } = model.attributes; const { style } = el; - this.frame.rect = 0; + this.frame.rect = undefined; style.left = isNaN(x) ? x : `${x}px`; style.top = isNaN(y) ? y : `${y}px`; md && this.updateOffset(); @@ -108,7 +112,7 @@ export default class FrameWrapView extends View { updateDim() { const { em, el, $el, model, classAnim, frame } = this; if (!frame) return; - frame.rect = 0; + frame.rect = undefined; $el.addClass(classAnim); const { noChanges, width, height } = this.__handleSize(); @@ -219,7 +223,7 @@ export default class FrameWrapView extends View { ` ); this.elTools = elTools; - const twrp = cv.toolsWrapper; + const twrp = cv?.toolsWrapper; twrp && twrp.appendChild(elTools); // TODO remove on frame remove onRender && onRender({ diff --git a/src/canvas/view/FramesView.js b/src/canvas/view/FramesView.js deleted file mode 100644 index 481367399..000000000 --- a/src/canvas/view/FramesView.js +++ /dev/null @@ -1,21 +0,0 @@ -import DomainViews from '../../domain_abstract/view/DomainViews'; -import FrameWrapView from './FrameWrapView'; - -export default class FramesView extends DomainViews { - constructor(opts = {}, config) { - super(opts, config, true); - this.listenTo(this.collection, 'reset', this.render); - } - - onRemoveBefore(items, opts) { - items.forEach(item => item.remove(opts)); - } - - onRender() { - const { $el } = this; - const { em } = this.collection.first; - em && $el.attr({ class: `${em.getConfig().stylePrefix}frames` }); - } -} - -FramesView.prototype.itemView = FrameWrapView; diff --git a/src/canvas/view/FramesView.ts b/src/canvas/view/FramesView.ts new file mode 100644 index 000000000..10761fedc --- /dev/null +++ b/src/canvas/view/FramesView.ts @@ -0,0 +1,26 @@ +import DomainViews from '../../abstract/DomainViews'; +import Frames from '../model/Frames'; +import CanvasView from './CanvasView'; +import FrameWrapView from './FrameWrapView'; + +export default class FramesView extends DomainViews { + canvasView: CanvasView; + constructor(opts = {}, config: any) { + super(opts, true); + //console.log(this.collection) + this.listenTo(this.collection, 'reset', this.render); + this.canvasView = config.canvasView + } + + onRemoveBefore(items: FrameWrapView[], opts = {}) { + items.forEach(item => item.remove(opts)); + } + + onRender() { + const { $el, em } = this; + em && $el.attr({ class: `${em.config.stylePrefix}frames` }); + } + protected renderView(item: any, type: string){return new FrameWrapView(item, this.canvasView)} +} + +//FramesView.prototype.itemView = FrameWrapView; diff --git a/src/dom_components/view/ComponentView.js b/src/dom_components/view/ComponentView.js index db589d255..ffbe1c0e0 100644 --- a/src/dom_components/view/ComponentView.js +++ b/src/dom_components/view/ComponentView.js @@ -457,7 +457,7 @@ export default class ComponentView extends Backbone.View { } _getFrame() { - return this.config.frameView; + return this.config.em?.get('Canvas').config.frameView; } /** diff --git a/src/editor/model/Editor.ts b/src/editor/model/Editor.ts index 4c192ea0c..06816051e 100644 --- a/src/editor/model/Editor.ts +++ b/src/editor/model/Editor.ts @@ -101,7 +101,6 @@ export default class EditorModel extends Model { get selected(): Selected { return this.get('selected'); } - constructor(conf = {}) { super(); this._config = conf; @@ -401,7 +400,7 @@ export default class EditorModel extends Model { * @param {Object} [opts={}] Options, optional * @public */ - setSelected(el: any | any[], opts: any = {}) { + setSelected(el?: any|any[], opts: any = {}) { const { event } = opts; const ctrlKey = event && (event.ctrlKey || event.metaKey); const { shiftKey } = event || {}; diff --git a/src/pages/model/Page.ts b/src/pages/model/Page.ts index c5f072dd8..d247b82ff 100644 --- a/src/pages/model/Page.ts +++ b/src/pages/model/Page.ts @@ -25,9 +25,8 @@ export default class Page extends Model { ['component', 'styles'].map((i) => this.unset(i)); } const frms: any[] = props.frames || [defFrame]; - const frames = new Frames( - frms?.map((model) => new Frame(model, opts)), - opts + const frames = new Frames(em.get("Canvas"), + frms?.map((model) => new Frame(em.get("Canvas"), model)) ); frames.page = this; this.set('frames', frames); @@ -37,7 +36,7 @@ export default class Page extends Model { } onRemove() { - this.get('frames').reset(); + this.getFrames().reset(); } getFrames(): Frames { @@ -88,7 +87,6 @@ export default class Page extends Model { * const mainFrame = page.getMainFrame(); */ getMainFrame(): Frame { - //@ts-ignore return this.getFrames().at(0); } diff --git a/src/utils/mixins.js b/src/utils/mixins.js index eea59a0a1..c12153fe6 100644 --- a/src/utils/mixins.js +++ b/src/utils/mixins.js @@ -85,7 +85,13 @@ const shallowDiff = (objOrig, objNew) => { return result; }; -const on = (el, ev, fn, opts) => { +/** + * @param {Object} el + * @param {string} ev + * @param {(ev: Event) => any} fn + * @param {Objec} opts + */ +const on = (el, ev, fn, opts = {}) => { ev = ev.split(/\s+/); el = el instanceof Array ? el : [el]; @@ -216,6 +222,11 @@ const getModel = (el, $) => { return model; }; +/** + * Get DomRect for the el + * @param {any} el Component or HTML element + * @return {DOMRect} + */ const getElRect = el => { const def = { top: 0, @@ -239,7 +250,7 @@ const getElRect = el => { /** * Get cross-device pointer event * @param {Event} ev - * @return {Event} + * @return {PointerEvent} */ const getPointerEvent = ev => (ev.touches && ev.touches[0] ? ev.touches[0] : ev);