UNPKG

grapesjs-clot

Version:

Free and Open Source Web Builder Framework

321 lines (289 loc) 9.28 kB
/** * You can customize the initial state of the module from the editor initialization * ```js * const editor = grapesjs.init({ * .... * pageManager: { * pages: [ * { * id: 'page-id', * styles: `.my-class { color: red }`, // or a JSON of styles * component: '<div class="my-class">My element</div>', // or a JSON of components * } * ] * }, * }) * ``` * * Once the editor is instantiated you can use its API. Before using these methods you should get the module from the instance * * ```js * const pageManager = editor.Pages; * ``` * * ## Available Events * * `page:add` - Added new page. The page is passed as an argument to the callback * * `page:remove` - Page removed. The page is passed as an argument to the callback * * `page:select` - New page selected. The newly selected page and the previous one, are passed as arguments to the callback * * `page:update` - Page updated. The updated page and the object containing changes are passed as arguments to the callback * * `page` - Catch-all event for all the events mentioned above. An object containing all the available data about the triggered event is passed as an argument to the callback * * ## Methods * * [add](#add) * * [get](#get) * * [getAll](#getall) * * [getAllWrappers](#getallwrappers) * * [getMain](#getmain) * * [remove](#remove) * * [select](#select) * * [getSelected](#getselected) * * [Page]: page.html * [Component]: component.html * * @module Pages */ import { isString, bindAll, unique, flatten } from 'underscore'; import { createId } from 'utils/mixins'; import { Model } from 'backbone'; import Pages from './model/Pages'; import Page from './model/Page'; export const evAll = 'page'; export const evPfx = `${evAll}:`; export const evPageSelect = `${evPfx}select`; export const evPageSelectBefore = `${evPageSelect}:before`; export const evPageUpdate = `${evPfx}update`; export const evPageAdd = `${evPfx}add`; export const evPageAddBefore = `${evPageAdd}:before`; export const evPageRemove = `${evPfx}remove`; export const evPageRemoveBefore = `${evPageRemove}:before`; const chnSel = 'change:selected'; const typeMain = 'main'; export default () => { return { name: 'PageManager', storageKey: 'pages', Page, Pages, events: { all: evAll, select: evPageSelect, selectBefore: evPageSelectBefore, update: evPageUpdate, add: evPageAdd, addBefore: evPageAddBefore, remove: evPageRemove, removeBefore: evPageRemoveBefore }, /** * Initialize module * @param {Object} config Configurations * @private */ init(opts = {}) { bindAll(this, '_onPageChange'); const { em } = opts; const cnf = { ...opts }; this.config = cnf; this.em = em; const pages = new Pages([], cnf); this.pages = pages; const model = new Model({ _undo: true }); this.model = model; pages.on('add', (p, c, o) => em.trigger(evPageAdd, p, o)); pages.on('remove', (p, c, o) => em.trigger(evPageRemove, p, o)); pages.on('change', (p, c) => { em.trigger(evPageUpdate, p, p.changedAttributes(), c); }); pages.on('reset', coll => coll.at(0) && this.select(coll.at(0))); pages.on('all', this.__onChange, this); model.on(chnSel, this._onPageChange); return this; }, __onChange(event, page, coll, opts) { const options = opts || coll; this.em.trigger(evAll, { event, page, options }); }, onLoad() { const { pages } = this; const opt = { silent: true }; pages.add(this.config.pages || [], opt); const mainPage = !pages.length ? this.add({ type: typeMain }, opt) : this.getMain(); this.select(mainPage, opt); }, _onPageChange(m, page, opts) { const { em } = this; const lm = em.get('LayerManager'); const mainComp = page.getMainComponent(); lm && mainComp && lm.setRoot(mainComp); em.trigger(evPageSelect, page, m.previous('selected')); this.__onChange(chnSel, page, opts); }, postLoad() { const { em, model } = this; const um = em.get('UndoManager'); um && um.add(model); um && um.add(this.pages); }, /** * Add new page * @param {Object} props Page properties * @param {Object} [opts] Options * @returns {[Page]} * @example * const newPage = pageManager.add({ * id: 'new-page-id', // without an explicit ID, a random one will be created * styles: `.my-class { color: red }`, // or a JSON of styles * component: '<div class="my-class">My element</div>', // or a JSON of components * }); */ add(props, opts = {}) { const { em } = this; props.id = props.id || this._createId(); const add = () => { const page = this.pages.add(props, opts); opts.select && this.select(page); return page; }; !opts.silent && em.trigger(evPageAddBefore, props, add, opts); return !opts.abort && add(); }, /** * Remove page * @param {String|[Page]} page Page or page id * @returns {[Page]} Removed Page * @example * const removedPage = pageManager.remove('page-id'); * // or by passing the page * const somePage = pageManager.get('page-id'); * pageManager.remove(somePage); */ remove(page, opts = {}) { const { em } = this; const pg = isString(page) ? this.get(page) : page; const rm = () => { pg && this.pages.remove(pg, opts); return pg; }; !opts.silent && em.trigger(evPageRemoveBefore, pg, rm, opts); return !opts.abort && rm(); }, /** * Get page by id * @param {String} id Page id * @returns {[Page]} * @example * const somePage = pageManager.get('page-id'); */ get(id) { return this.pages.filter(p => p.get('id') === id)[0]; }, /** * Get main page (the first one available) * @returns {[Page]} * @example * const mainPage = pageManager.getMain(); */ getMain() { const { pages } = this; return pages.filter(p => p.get('type') === typeMain)[0] || pages.at(0); }, /** * Get all pages * @returns {Array<[Page]>} * @example * const arrayOfPages = pageManager.getAll(); */ getAll() { return [...this.pages.models]; }, /** * Get wrapper components (aka body) from all pages and frames. * @returns {Array<[Component]>} * @example * const wrappers = pageManager.getAllWrappers(); * // Get all `image` components from the project * const allImages = wrappers.map(wrp => wrp.findType('image')).flat(); */ getAllWrappers() { const pages = this.getAll(); return unique( flatten( pages.map(page => page.getAllFrames().map(frame => frame.getComponent()) ) ) ); }, getAllMap() { return this.getAll().reduce((acc, i) => { acc[i.get('id')] = i; return acc; }, {}); }, /** * Change the selected page. This will switch the page rendered in canvas * @param {String|[Page]} page Page or page id * @returns {this} * @example * pageManager.select('page-id'); * // or by passing the page * const somePage = pageManager.get('page-id'); * pageManager.select(somePage); */ select(page, opts = {}) { const pg = isString(page) ? this.get(page) : page; if (pg) { this.em.trigger(evPageSelectBefore, pg, opts); this.model.set('selected', pg, opts); } return this; }, /** * Get the selected page * @returns {[Page]} * @example * const selectedPage = pageManager.getSelected(); */ getSelected() { return this.model.get('selected'); }, destroy() { this.pages.off().reset(); this.model.stopListening(); this.model.clear({ silent: true }); ['selected', 'config', 'em', 'pages', 'model'].map(i => (this[i] = 0)); }, store(noStore) { if (!this.em.get('hasPages')) return {}; const obj = {}; const cnf = this.config; obj[this.storageKey] = JSON.stringify(this.getAll()); if (!noStore && cnf.stm) cnf.stm.store(obj); return obj; }, load(data = {}) { const key = this.storageKey; let res = data[key] || []; if (typeof res == 'string') { try { res = JSON.parse(data[key]); } catch (err) {} } res && res.length && this.pages.reset(res); return res; }, _createId() { const pages = this.getAll(); const len = pages.length + 16; const pagesMap = this.getAllMap(); let id; do { id = createId(len); } while (pagesMap[id]); return id; } }; };