UNPKG

@print-one/grapesjs

Version:

Free and Open Source Web Builder Framework

283 lines (236 loc) 7.09 kB
import { forEach, isEmpty, isNumber, isString, result } from 'underscore'; import CanvasModule from '..'; import { ModuleModel } from '../../abstract'; import { BoxRect } from '../../common'; import ComponentWrapper from '../../dom_components/model/ComponentWrapper'; import Page from '../../pages/model/Page'; import { createId, isComponent, isObject } from '../../utils/mixins'; import FrameView from '../view/FrameView'; import Frames from './Frames'; const keyAutoW = '__aw'; const keyAutoH = '__ah'; const getDimension = (frame: Frame, type: 'width' | 'height') => { const dim = frame.get(type); const viewDim = frame.view?.el[type === 'width' ? 'offsetWidth' : 'offsetHeight']; if (isNumber(dim)) { return dim; } else if (isString(dim) && dim.endsWith('px')) { return parseFloat(dim); } else if (viewDim) { return viewDim; } else { return 0; } }; /** * @property {Object|String} component Wrapper component definition. You can also pass an HTML string as components of the default wrapper component. * @property {String} [width=''] Width of the frame. By default, the canvas width will be taken. * @property {String} [height=''] Height of the frame. By default, the canvas height will be taken. * @property {Number} [x=0] Horizontal position of the frame in the canvas. * @property {Number} [y=0] Vertical position of the frame in the canvas. * */ export default class Frame extends ModuleModel<CanvasModule> { defaults() { return { x: 0, y: 0, changesCount: 0, attributes: {}, width: null, height: null, head: [], component: '', styles: '', refFrame: null, _undo: true, _undoexc: ['changesCount'], }; } view?: FrameView; /** * @hideconstructor */ constructor(module: CanvasModule, attr: any) { super(module, attr); const { em } = this; const { styles, component } = this.attributes; const domc = em.Components; const conf = domc.getConfig(); const allRules = em.Css.getAll(); const idMap: any = {}; const modOpts = { em, config: conf, frame: this, idMap }; if (!isComponent(component)) { const wrp = isObject(component) ? component : { components: component }; !wrp.type && (wrp.type = 'wrapper'); const Wrapper = domc.getType('wrapper')!.model; this.set('component', new Wrapper(wrp, modOpts)); } if (!styles) { this.set('styles', allRules); } else if (!isObject(styles)) { // Avoid losing styles on remapped components const idMapKeys = Object.keys(idMap); if (idMapKeys.length && Array.isArray(styles)) { styles.forEach(style => { const sel = style.selectors; if (sel && sel.length == 1) { const sSel = sel[0]; const idSel = sSel.name && sSel.type === 2 && sSel; if (idSel && idMap[idSel.name]) { idSel.name = idMap[idSel.name]; } else if (isString(sSel) && sSel[0] === '#') { const prevId = sSel.substring(1); if (prevId && idMap[prevId]) { sel[0] = `#${idMap[prevId]}`; } } } }); } allRules.add(styles); this.set('styles', allRules); } !attr.width && this.set(keyAutoW, 1); !attr.height && this.set(keyAutoH, 1); !this.id && this.set('id', createId()); } get width() { return getDimension(this, 'width'); } get height() { return getDimension(this, 'height'); } get head(): { tag: string; attributes: any }[] { return this.get('head'); } get refFrame(): Frame | undefined { return this.get('refFrame'); } get root() { const { refFrame } = this; return refFrame?.getComponent() || this.getComponent(); } initRefs() { const { refFrame } = this; if (isString(refFrame)) { const frame = this.module.framesById[refFrame]; frame && this.set({ refFrame: frame }, { silent: true }); } } getBoxRect(): BoxRect { const { x, y } = this.attributes; const { width, height } = this; return { x, y, width, height, }; } onRemove() { !this.refFrame && this.getComponent().remove({ root: 1 }); } changesUp(opt: any = {}) { if (opt.temporary || opt.noCount || opt.avoidStore) { return; } this.set('changesCount', this.get('changesCount') + 1); } getComponent(): ComponentWrapper { return this.get('component'); } getStyles() { return this.get('styles'); } disable() { this.trigger('disable'); } remove() { this.view?.remove(); this.view = undefined; const coll = this.collection; return coll && coll.remove(this); } getHead() { return [...this.head]; } setHead(value: { tag: string; attributes: any }[]) { return this.set('head', [...value]); } addHeadItem(item: { tag: string; attributes: any }) { this.head.push(item); } getHeadByAttr(attr: string, value: any, tag: string) { return this.head.filter(item => item.attributes && item.attributes[attr] == value && (!tag || tag === item.tag))[0]; } removeHeadByAttr(attr: string, value: any, tag: string) { const item = this.getHeadByAttr(attr, value, tag); const index = this.head.indexOf(item); if (index >= 0) { this.head.splice(index, 1); } } addLink(href: string) { const tag = 'link'; !this.getHeadByAttr('href', href, tag) && this.addHeadItem({ tag, attributes: { href, rel: 'stylesheet', }, }); } removeLink(href: string) { this.removeHeadByAttr('href', href, 'link'); } addScript(src: string) { const tag = 'script'; !this.getHeadByAttr('src', src, tag) && this.addHeadItem({ tag, attributes: { src }, }); } removeScript(src: string) { this.removeHeadByAttr('src', src, 'script'); } getPage(): Page | undefined { return (this.collection as unknown as Frames)?.page; } _emitUpdated(data = {}) { this.em.trigger('frame:updated', { frame: this, ...data }); } hasAutoHeight() { const { height } = this.attributes; if (height === 'auto' || this.config.infiniteCanvas) { return true; } return false; } toJSON(opts: any = {}) { const obj = ModuleModel.prototype.toJSON.call(this, opts); const defaults = result(this, 'defaults'); if (opts.fromUndo) delete obj.component; delete obj.styles; delete obj.changesCount; obj[keyAutoW] && delete obj.width; obj[keyAutoH] && delete obj.height; if (obj.refFrame) { obj.refFrame = obj.refFrame.id; delete obj.component; } // Remove private keys forEach(obj, (value, key) => { key.indexOf('_') === 0 && delete obj[key]; }); forEach(defaults, (value, key) => { if (obj[key] === value) delete obj[key]; }); forEach(['attributes', 'head'], prop => { if (isEmpty(obj[prop])) delete obj[prop]; }); return obj; } }