This commit is contained in:
Yuri Kuznetsov
2023-07-16 17:08:25 +03:00
parent 26d2092ace
commit 3f25833d3d
5 changed files with 151 additions and 117 deletions
+126 -97
View File
@@ -30,12 +30,18 @@
import Exceptions from 'exceptions';
import {Events, View as BullView} from 'bullbone';
import $ from 'jquery';
/**
* @callback module:controller~viewCallback
* @param {module:view} view A view.
*/
/**
* @callback module:controller~masterViewCallback
* @param {module:views/site/master} view A master view.
*/
/**
* A controller. To be extended.
*
@@ -398,12 +404,13 @@ class Controller {
}
/**
* Create a master view, render if not already rendered.
* Serve a master view. Render if not already rendered.
*
* @param {module:controller~viewCallback} callback A callback with a created master view.
* @param {module:controller~masterViewCallback} callback A callback with a created master view.
* @private
*/
master(callback) {
let entire = this.get('entire');
const entire = this.get('entire');
if (entire) {
entire.remove();
@@ -411,7 +418,7 @@ class Controller {
this.set('entire', null);
}
let master = this.get('master');
const master = this.get('master');
if (master) {
callback.call(this, master);
@@ -421,7 +428,7 @@ class Controller {
let masterView = this.masterView || 'views/site/master';
this.viewFactory.create(masterView, {fullSelector: 'body'}, /** Bull.View */master => {
this.viewFactory.create(masterView, {fullSelector: 'body'}, /** module:view */master => {
this.set('master', master);
if (this.get('masterRendered')) {
@@ -439,132 +446,153 @@ class Controller {
});
}
/**
* @typedef {Object} module:controller~mainParams
* @property {boolean} [useStored] Use a stored view if available.
* @property {string} [key] A stored view key.
*/
/**
* Create a main view in the master.
*
* @param {string} [view] A view name.
* @param {Object} [options] Options for view.
* @param {string|module:view} [view] A view name.
* @param {Object.<string, *>} [options] Options for a view.
* @param {module:controller~viewCallback} [callback] A callback with a created view.
* @param {boolean} [useStored] Use a stored view if available.
* @param {string} [storedKey] A stored view key.
* @param {module:controller~mainParams} [params] Parameters.
*/
main(view, options, callback, useStored, storedKey) {
let isCanceled = false;
let isRendered = false;
main(view, options, callback, params = {}) {
const dto = {
isCanceled: false,
key: params.key,
useStored: params.useStored,
callback: callback,
};
this.listenToOnce(this.baseController, 'action', () => {
isCanceled = true;
});
const useStored = params.useStored || false;
const key = params.key;
this.listenToOnce(this.baseController, 'action', () => dto.isCanceled = true);
view = view || 'views/base';
this.master(master => {
if (isCanceled) {
this.master(masterView => {
if (dto.isCanceled) {
return;
}
options = options || {};
options.fullSelector = '#main';
let process = main => {
if (isCanceled) {
return;
}
if (storedKey) {
this.storeMainView(storedKey, main);
}
const onAction = () => {
main.cancelRender();
isCanceled = true;
};
main.listenToOnce(this.baseController, 'action', onAction);
if (master.currentViewKey) {
this.set('storedScrollTop-' + master.currentViewKey, $(window).scrollTop());
if (this.hasStoredMainView(master.currentViewKey)) {
let mainView = master.getView('main');
if (mainView) {
mainView.propagateEvent('remove', {ignoreCleaning: true});
}
master.unchainView('main');
}
}
master.currentViewKey = storedKey;
master.setView('main', main);
let afterRender = () => {
setTimeout(() => main.stopListening(this.baseController, 'action', onAction), 500);
isRendered = true;
main.updatePageTitle();
if (useStored && this.has('storedScrollTop-' + storedKey)) {
$(window).scrollTop(this.get('storedScrollTop-' + storedKey));
return;
}
$(window).scrollTop(0);
};
if (callback) {
this.listenToOnce(main, 'after:render', afterRender);
callback.call(this, main);
return;
}
main.render()
.then(afterRender);
};
if (useStored && this.hasStoredMainView(storedKey)) {
let main = this.getStoredMainView(storedKey);
if (useStored && this.hasStoredMainView(key)) {
let mainView = this.getStoredMainView(key);
let isActual = true;
if (
main &&
('isActualForReuse' in main) &&
typeof main.isActualForReuse === 'function'
mainView &&
('isActualForReuse' in mainView) &&
typeof mainView.isActualForReuse === 'function'
) {
isActual = main.isActualForReuse();
isActual = mainView.isActualForReuse();
}
let lastUrl = (main && 'lastUrl' in main) ? main.lastUrl : null;
let lastUrl = (mainView && 'lastUrl' in mainView) ? mainView.lastUrl : null;
if (
isActual &&
(!lastUrl || lastUrl === this.getRouter().getCurrentUrl())
) {
process(main);
this._processMain(mainView, masterView, dto);
if (
'setupReuse' in main &&
typeof main.setupReuse === 'function'
'setupReuse' in mainView &&
typeof mainView.setupReuse === 'function'
) {
main.setupReuse(options.params || {});
mainView.setupReuse(options.params || {});
}
return;
}
this.clearStoredMainView(storedKey);
this.clearStoredMainView(key);
}
this.viewFactory.create(view, options, process);
this.viewFactory.create(view, options, view => this._processMain(view, masterView, dto));
});
}
/**
* @param {module:view} mainView
* @param {module:views/site/master} masterView
* @param {{
* isCanceled: boolean,
* key?: string,
* useStored?: boolean,
* callback?: module:controller~viewCallback,
* }} dto Data.
* @private
*/
_processMain(mainView, masterView, dto) {
if (dto.isCanceled) {
return;
}
const key = dto.key;
if (key) {
this.storeMainView(key, mainView);
}
const onAction = () => {
mainView.cancelRender();
dto.isCanceled = true;
};
mainView.listenToOnce(this.baseController, 'action', onAction);
if (masterView.currentViewKey) {
this.set('storedScrollTop-' + masterView.currentViewKey, $(window).scrollTop());
if (this.hasStoredMainView(masterView.currentViewKey)) {
let mainView = masterView.getView('main');
if (mainView) {
mainView.propagateEvent('remove', {ignoreCleaning: true});
}
masterView.unchainView('main');
}
}
masterView.currentViewKey = key;
masterView.setView('main', mainView);
const afterRender = () => {
setTimeout(() => mainView.stopListening(this.baseController, 'action', onAction), 500);
mainView.updatePageTitle();
if (dto.useStored && this.has('storedScrollTop-' + key)) {
$(window).scrollTop(this.get('storedScrollTop-' + key));
return;
}
$(window).scrollTop(0);
};
if (dto.callback) {
this.listenToOnce(mainView, 'after:render', afterRender);
dto.callback.call(this, mainView);
return;
}
mainView.render()
.then(afterRender);
}
/**
* Show a loading notify-message.
*/
@@ -592,17 +620,18 @@ class Controller {
}
/**
* Create a view in the <body> element.
* Create a view in the BODY element.
*
* @param {String} view A view name.
* @param {Bull.View~Options} options Options for a view.
* @todo Add support of view instances.
* @param {string} view A view name.
* @param {Object.<string, *>} [options] Options for a view.
* @param {module:controller~viewCallback} [callback] A callback with a created view.
*/
entire(view, options, callback) {
let master = this.get('master');
const masterView = this.get('master');
if (master) {
master.remove();
if (masterView) {
masterView.remove();
}
this.set('master', null);
+1
View File
@@ -28,6 +28,7 @@
import Controller from 'controller';
import SearchManager from 'search-manager';
import SettingsEditView from 'views/settings/edit';
class AdminController extends Controller {
+8 -7
View File
@@ -103,13 +103,14 @@ class RecordController extends Controller {
mediator: mediator,
};
this.main(
this.getViewName('list'),
viewOptions,
null,
isReturn,
key
);
const viewName = this.getViewName('list');
const params = {
useStored: isReturn,
key: key,
};
this.main(viewName, viewOptions, null, params);
});
}
+14 -13
View File
@@ -26,19 +26,20 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
define('views/settings/edit', ['views/edit'], function (Dep) {
import EditView from 'views/edit';
return Dep.extend({
class SettingsEditView extends EditView {
scope: 'Settings',
scope = 'Settings'
setupHeader: function () {
this.createView('header', this.headerView, {
model: this.model,
fullSelector: '#main > .header',
template: this.options.headerTemplate,
label: this.options.label,
});
},
});
});
setupHeader() {
this.createView('header', this.headerView, {
model: this.model,
fullSelector: '#main > .header',
template: this.options.headerTemplate,
label: this.options.label,
});
}
}
export default SettingsEditView;
+2
View File
@@ -26,6 +26,8 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
/** @module views/site/master */
import View from 'view';
import $ from 'jquery';