|
|
@ -46,9 +46,11 @@ |
|
|
|
|
|
|
|
|
import { isString, bindAll, unique, flatten } from 'underscore'; |
|
|
import { isString, bindAll, unique, flatten } from 'underscore'; |
|
|
import { createId } from '../utils/mixins'; |
|
|
import { createId } from '../utils/mixins'; |
|
|
import { Model, Module } from '../common'; |
|
|
import { Model, Module } from '../abstract'; |
|
|
|
|
|
import { ItemManagerModule } from '../abstract/Module'; |
|
|
import Pages from './model/Pages'; |
|
|
import Pages from './model/Pages'; |
|
|
import Page from './model/Page'; |
|
|
import Page from './model/Page'; |
|
|
|
|
|
import EditorModel from '../editor/model/Editor'; |
|
|
|
|
|
|
|
|
export const evAll = 'page'; |
|
|
export const evAll = 'page'; |
|
|
export const evPfx = `${evAll}:`; |
|
|
export const evPfx = `${evAll}:`; |
|
|
@ -61,239 +63,226 @@ export const evPageRemove = `${evPfx}remove`; |
|
|
export const evPageRemoveBefore = `${evPageRemove}:before`; |
|
|
export const evPageRemoveBefore = `${evPageRemove}:before`; |
|
|
const chnSel = 'change:selected'; |
|
|
const chnSel = 'change:selected'; |
|
|
const typeMain = 'main'; |
|
|
const typeMain = 'main'; |
|
|
|
|
|
const events = { |
|
|
|
|
|
all: evAll, |
|
|
|
|
|
select: evPageSelect, |
|
|
|
|
|
selectBefore: evPageSelectBefore, |
|
|
|
|
|
update: evPageUpdate, |
|
|
|
|
|
add: evPageAdd, |
|
|
|
|
|
addBefore: evPageAddBefore, |
|
|
|
|
|
remove: evPageRemove, |
|
|
|
|
|
removeBefore: evPageRemoveBefore, |
|
|
|
|
|
}; |
|
|
|
|
|
export default class PageManager extends ItemManagerModule { |
|
|
|
|
|
name = 'PageManager'; |
|
|
|
|
|
|
|
|
export default () => { |
|
|
storageKey = 'pages'; |
|
|
return { |
|
|
|
|
|
...Module, |
|
|
|
|
|
|
|
|
|
|
|
name: 'PageManager', |
|
|
|
|
|
|
|
|
|
|
|
storageKey: 'pages', |
|
|
|
|
|
|
|
|
|
|
|
Page, |
|
|
|
|
|
|
|
|
|
|
|
Pages, |
|
|
Page; |
|
|
|
|
|
|
|
|
events: { |
|
|
Pages; |
|
|
all: evAll, |
|
|
|
|
|
select: evPageSelect, |
|
|
|
|
|
selectBefore: evPageSelectBefore, |
|
|
|
|
|
update: evPageUpdate, |
|
|
|
|
|
add: evPageAdd, |
|
|
|
|
|
addBefore: evPageAddBefore, |
|
|
|
|
|
remove: evPageRemove, |
|
|
|
|
|
removeBefore: evPageRemoveBefore, |
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
get pages() { |
|
|
* Initialize module |
|
|
return this.all; |
|
|
* @param {Object} config Configurations |
|
|
} |
|
|
* @private |
|
|
/** |
|
|
*/ |
|
|
* Initialize module |
|
|
init(opts = {}) { |
|
|
* @param {Object} config Configurations |
|
|
bindAll(this, '_onPageChange'); |
|
|
* @private |
|
|
const { em } = opts; |
|
|
*/ |
|
|
const cnf = { ...opts }; |
|
|
constructor(em) { |
|
|
this.config = cnf; |
|
|
const pages = new Pages([]); |
|
|
this.em = em; |
|
|
super(em, 'PageManager', pages, events); |
|
|
const pages = new Pages([]); |
|
|
bindAll(this, '_onPageChange'); |
|
|
this.pages = pages; |
|
|
const model = new Model({ _undo: true }); |
|
|
this.all = pages; |
|
|
this.model = model; |
|
|
const model = new Model({ _undo: true }); |
|
|
pages.on('reset', coll => coll.at(0) && this.select(coll.at(0))); |
|
|
this.model = model; |
|
|
pages.on('all', this.__onChange, this); |
|
|
pages.on('add', (p, c, o) => em.trigger(evPageAdd, p, o)); |
|
|
model.on(chnSel, this._onPageChange); |
|
|
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; |
|
|
return this; |
|
|
}, |
|
|
} |
|
|
|
|
|
|
|
|
__onChange(event, page, coll, opts) { |
|
|
__onChange(event, page, coll, opts) { |
|
|
const options = opts || coll; |
|
|
const options = opts || coll; |
|
|
this.em.trigger(evAll, { event, page, options }); |
|
|
this.em.trigger(evAll, { event, page, options }); |
|
|
}, |
|
|
} |
|
|
|
|
|
|
|
|
onLoad() { |
|
|
onLoad() { |
|
|
const { pages } = this; |
|
|
const { pages } = this; |
|
|
const opt = { silent: true }; |
|
|
const opt = { silent: true }; |
|
|
pages.add(this.config.pages?.map(page => new Page(page, { em: this.em, config: this.config })) || [], opt); |
|
|
pages.add(this.config.pages?.map(page => new Page(page, { em: this.em, config: this.config })) || [], opt); |
|
|
const mainPage = !pages.length ? this.add({ type: typeMain }, opt) : this.getMain(); |
|
|
const mainPage = !pages.length ? this.add({ type: typeMain }, opt) : this.getMain(); |
|
|
this.select(mainPage, opt); |
|
|
this.select(mainPage, opt); |
|
|
}, |
|
|
} |
|
|
|
|
|
|
|
|
_onPageChange(m, page, opts) { |
|
|
_onPageChange(m, page, opts) { |
|
|
const { em } = this; |
|
|
const { em } = this; |
|
|
const lm = em.get('LayerManager'); |
|
|
const lm = em.get('LayerManager'); |
|
|
const mainComp = page.getMainComponent(); |
|
|
const mainComp = page.getMainComponent(); |
|
|
lm && mainComp && lm.setRoot(mainComp); |
|
|
lm && mainComp && lm.setRoot(mainComp); |
|
|
em.trigger(evPageSelect, page, m.previous('selected')); |
|
|
em.trigger(evPageSelect, page, m.previous('selected')); |
|
|
this.__onChange(chnSel, page, opts); |
|
|
this.__onChange(chnSel, page, opts); |
|
|
}, |
|
|
} |
|
|
|
|
|
|
|
|
postLoad() { |
|
|
postLoad() { |
|
|
const { em, model } = this; |
|
|
const { em, model } = this; |
|
|
const um = em.get('UndoManager'); |
|
|
const um = em.get('UndoManager'); |
|
|
um && um.add(model); |
|
|
um && um.add(model); |
|
|
um && um.add(this.pages); |
|
|
um && um.add(this.pages); |
|
|
}, |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Add new page |
|
|
* Add new page |
|
|
* @param {Object} props Page properties |
|
|
* @param {Object} props Page properties |
|
|
* @param {Object} [opts] Options |
|
|
* @param {Object} [opts] Options |
|
|
* @returns {[Page]} |
|
|
* @returns {[Page]} |
|
|
* @example |
|
|
* @example |
|
|
* const newPage = pageManager.add({ |
|
|
* const newPage = pageManager.add({ |
|
|
* id: 'new-page-id', // without an explicit ID, a random one will be created
|
|
|
* id: 'new-page-id', // without an explicit ID, a random one will be created
|
|
|
* styles: `.my-class { color: red }`, // or a JSON of styles
|
|
|
* styles: `.my-class { color: red }`, // or a JSON of styles
|
|
|
* component: '<div class="my-class">My element</div>', // or a JSON of components
|
|
|
* component: '<div class="my-class">My element</div>', // or a JSON of components
|
|
|
* }); |
|
|
* }); |
|
|
*/ |
|
|
*/ |
|
|
add(props, opts = {}) { |
|
|
add(props, opts = {}) { |
|
|
const { em } = this; |
|
|
const { em } = this; |
|
|
props.id = props.id || this._createId(); |
|
|
props.id = props.id || this._createId(); |
|
|
const add = () => { |
|
|
const add = () => { |
|
|
const page = this.pages.add(new Page(props, { em: this.em, config: this.config }), opts); |
|
|
const page = this.pages.add(new Page(props, { em: this.em, config: this.config }), opts); |
|
|
opts.select && this.select(page); |
|
|
opts.select && this.select(page); |
|
|
return page; |
|
|
return page; |
|
|
}; |
|
|
}; |
|
|
!opts.silent && em.trigger(evPageAddBefore, props, add, opts); |
|
|
!opts.silent && em.trigger(evPageAddBefore, props, add, opts); |
|
|
return !opts.abort && add(); |
|
|
return !opts.abort && add(); |
|
|
}, |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Remove page |
|
|
* Remove page |
|
|
* @param {String|[Page]} page Page or page id |
|
|
* @param {String|[Page]} page Page or page id |
|
|
* @returns {[Page]} Removed Page |
|
|
* @returns {[Page]} Removed Page |
|
|
* @example |
|
|
* @example |
|
|
* const removedPage = pageManager.remove('page-id'); |
|
|
* const removedPage = pageManager.remove('page-id'); |
|
|
* // or by passing the page
|
|
|
* // or by passing the page
|
|
|
* const somePage = pageManager.get('page-id'); |
|
|
* const somePage = pageManager.get('page-id'); |
|
|
* pageManager.remove(somePage); |
|
|
* pageManager.remove(somePage); |
|
|
*/ |
|
|
*/ |
|
|
remove(page, opts = {}) { |
|
|
remove(page, opts = {}) { |
|
|
const { em } = this; |
|
|
const { em } = this; |
|
|
const pg = isString(page) ? this.get(page) : page; |
|
|
const pg = isString(page) ? this.get(page) : page; |
|
|
const rm = () => { |
|
|
const rm = () => { |
|
|
pg && this.pages.remove(pg, opts); |
|
|
pg && this.pages.remove(pg, opts); |
|
|
return pg; |
|
|
return pg; |
|
|
}; |
|
|
}; |
|
|
!opts.silent && em.trigger(evPageRemoveBefore, pg, rm, opts); |
|
|
!opts.silent && em.trigger(evPageRemoveBefore, pg, rm, opts); |
|
|
return !opts.abort && rm(); |
|
|
return !opts.abort && rm(); |
|
|
}, |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Get page by id |
|
|
* Get page by id |
|
|
* @param {String} id Page id |
|
|
* @param {String} id Page id |
|
|
* @returns {[Page]} |
|
|
* @returns {[Page]} |
|
|
* @example |
|
|
* @example |
|
|
* const somePage = pageManager.get('page-id'); |
|
|
* const somePage = pageManager.get('page-id'); |
|
|
*/ |
|
|
*/ |
|
|
get(id) { |
|
|
get(id) { |
|
|
return this.pages.filter(p => p.get('id') === id)[0]; |
|
|
return this.pages.filter(p => p.get('id') === id)[0]; |
|
|
}, |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Get main page (the first one available) |
|
|
* Get main page (the first one available) |
|
|
* @returns {[Page]} |
|
|
* @returns {[Page]} |
|
|
* @example |
|
|
* @example |
|
|
* const mainPage = pageManager.getMain(); |
|
|
* const mainPage = pageManager.getMain(); |
|
|
*/ |
|
|
*/ |
|
|
getMain() { |
|
|
getMain() { |
|
|
const { pages } = this; |
|
|
const { pages } = this; |
|
|
return pages.filter(p => p.get('type') === typeMain)[0] || pages.at(0); |
|
|
return pages.filter(p => p.get('type') === typeMain)[0] || pages.at(0); |
|
|
}, |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Get all pages |
|
|
* Get all pages |
|
|
* @returns {Array<[Page]>} |
|
|
* @returns {Array<[Page]>} |
|
|
* @example |
|
|
* @example |
|
|
* const arrayOfPages = pageManager.getAll(); |
|
|
* const arrayOfPages = pageManager.getAll(); |
|
|
*/ |
|
|
*/ |
|
|
getAll() { |
|
|
getAll() { |
|
|
return [...this.pages.models]; |
|
|
return [...this.pages.models]; |
|
|
}, |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Get wrapper components (aka body) from all pages and frames. |
|
|
* Get wrapper components (aka body) from all pages and frames. |
|
|
* @returns {Array<[Component]>} |
|
|
* @returns {Array<[Component]>} |
|
|
* @example |
|
|
* @example |
|
|
* const wrappers = pageManager.getAllWrappers(); |
|
|
* const wrappers = pageManager.getAllWrappers(); |
|
|
* // Get all `image` components from the project
|
|
|
* // Get all `image` components from the project
|
|
|
* const allImages = wrappers.map(wrp => wrp.findType('image')).flat(); |
|
|
* const allImages = wrappers.map(wrp => wrp.findType('image')).flat(); |
|
|
*/ |
|
|
*/ |
|
|
getAllWrappers() { |
|
|
getAllWrappers() { |
|
|
const pages = this.getAll(); |
|
|
const pages = this.getAll(); |
|
|
return unique(flatten(pages.map(page => page.getAllFrames().map(frame => frame.getComponent())))); |
|
|
return unique(flatten(pages.map(page => page.getAllFrames().map(frame => frame.getComponent())))); |
|
|
}, |
|
|
} |
|
|
|
|
|
|
|
|
getAllMap() { |
|
|
getAllMap() { |
|
|
return this.getAll().reduce((acc, i) => { |
|
|
return this.getAll().reduce((acc, i) => { |
|
|
acc[i.get('id')] = i; |
|
|
acc[i.get('id')] = i; |
|
|
return acc; |
|
|
return acc; |
|
|
}, {}); |
|
|
}, {}); |
|
|
}, |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Change the selected page. This will switch the page rendered in canvas |
|
|
* Change the selected page. This will switch the page rendered in canvas |
|
|
* @param {String|[Page]} page Page or page id |
|
|
* @param {String|[Page]} page Page or page id |
|
|
* @returns {this} |
|
|
* @returns {this} |
|
|
* @example |
|
|
* @example |
|
|
* pageManager.select('page-id'); |
|
|
* pageManager.select('page-id'); |
|
|
* // or by passing the page
|
|
|
* // or by passing the page
|
|
|
* const somePage = pageManager.get('page-id'); |
|
|
* const somePage = pageManager.get('page-id'); |
|
|
* pageManager.select(somePage); |
|
|
* pageManager.select(somePage); |
|
|
*/ |
|
|
*/ |
|
|
select(page, opts = {}) { |
|
|
select(page, opts = {}) { |
|
|
const pg = isString(page) ? this.get(page) : page; |
|
|
const pg = isString(page) ? this.get(page) : page; |
|
|
if (pg) { |
|
|
if (pg) { |
|
|
this.em.trigger(evPageSelectBefore, pg, opts); |
|
|
this.em.trigger(evPageSelectBefore, pg, opts); |
|
|
this.model.set('selected', pg, opts); |
|
|
this.model.set('selected', pg, opts); |
|
|
} |
|
|
} |
|
|
return this; |
|
|
return this; |
|
|
}, |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Get the selected page |
|
|
* Get the selected page |
|
|
* @returns {[Page]} |
|
|
* @returns {[Page]} |
|
|
* @example |
|
|
* @example |
|
|
* const selectedPage = pageManager.getSelected(); |
|
|
* const selectedPage = pageManager.getSelected(); |
|
|
*/ |
|
|
*/ |
|
|
getSelected() { |
|
|
getSelected() { |
|
|
return this.model.get('selected'); |
|
|
return this.model.get('selected'); |
|
|
}, |
|
|
} |
|
|
|
|
|
|
|
|
destroy() { |
|
|
destroy() { |
|
|
this.pages.off().reset(); |
|
|
this.pages.off().reset(); |
|
|
this.model.stopListening(); |
|
|
this.model.stopListening(); |
|
|
this.model.clear({ silent: true }); |
|
|
this.model.clear({ silent: true }); |
|
|
['selected', 'config', 'em', 'pages', 'model'].map(i => (this[i] = 0)); |
|
|
['selected', 'model'].map(i => (this[i] = 0)); |
|
|
}, |
|
|
} |
|
|
|
|
|
|
|
|
store() { |
|
|
store() { |
|
|
return this.getProjectData(); |
|
|
return this.getProjectData(); |
|
|
}, |
|
|
} |
|
|
|
|
|
|
|
|
load(data) { |
|
|
load(data) { |
|
|
return this.loadProjectData(data, { all: this.pages, reset: true }); |
|
|
return this.loadProjectData(data, { all: this.pages, reset: true }); |
|
|
}, |
|
|
} |
|
|
|
|
|
|
|
|
_createId() { |
|
|
_createId() { |
|
|
const pages = this.getAll(); |
|
|
const pages = this.getAll(); |
|
|
const len = pages.length + 16; |
|
|
const len = pages.length + 16; |
|
|
const pagesMap = this.getAllMap(); |
|
|
const pagesMap = this.getAllMap(); |
|
|
let id; |
|
|
let id; |
|
|
|
|
|
|
|
|
do { |
|
|
do { |
|
|
id = createId(len); |
|
|
id = createId(len); |
|
|
} while (pagesMap[id]); |
|
|
} while (pagesMap[id]); |
|
|
|
|
|
|
|
|
return id; |
|
|
return id; |
|
|
}, |
|
|
} |
|
|
}; |
|
|
} |
|
|
}; |
|
|
|
|
|
|