unclean SPARC
This commit is contained in:
Executable
+347
@@ -0,0 +1,347 @@
|
||||
/**
|
||||
* @category MyEic
|
||||
* @subcategory Libraries
|
||||
* @extends Controller
|
||||
*/
|
||||
class EICController extends Controller {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.view2url = {};
|
||||
}
|
||||
|
||||
getViewByClass(className) {
|
||||
let content = Controller._contents.find(o => o.view._className == className)
|
||||
return content ? content.view: null;
|
||||
}
|
||||
|
||||
getViewByURL(url) {
|
||||
let content = Controller._contents.find(o => o.view._url == url)
|
||||
return content ? content.view: null;
|
||||
}
|
||||
|
||||
loadContent(name, options, data) {
|
||||
options = options || {};
|
||||
options.onContentLoaded = this.createContent;
|
||||
return super.loadView(name, options, data);
|
||||
}
|
||||
|
||||
createContent(options, data, html) {
|
||||
|
||||
let container = ui.create('<div></div>');
|
||||
container.innerHTML = Controller.processTemplate(options.name, html, data);
|
||||
|
||||
let view = new app.LoadedClasses[options.className](options);
|
||||
view._className = options.name.replace('.html', '');
|
||||
view._controller = this;
|
||||
view.el = container;
|
||||
view.DOMContentLoaded(data);
|
||||
|
||||
container.setAttribute('sparc-id', view._sparcId);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
loadWindow(name, options, data) {
|
||||
let url = app.Router.currentRoute.realUrl;
|
||||
options = options || {};
|
||||
// if static, redirect to existing instance
|
||||
if(options.static) {
|
||||
/*
|
||||
MFA:
|
||||
Using stored real url instead of classname allows different instance of window based on parameter:
|
||||
eg:
|
||||
having route "/window/{id}", calling "/window/345" won't replace "/window/123"
|
||||
|
||||
Makes me wonder: shouldn't all windows be static anyway then ?
|
||||
*/
|
||||
//let existing = this.getViewByClass(name);
|
||||
let existing = this.getViewByURL(url);
|
||||
|
||||
if(existing) {
|
||||
this.focus(existing._sparcId, data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
options.onContentLoaded = this.createWindow;
|
||||
super.loadView(name, options, data).then(
|
||||
view => {
|
||||
this.view2url[view._sparcId] = url;
|
||||
view._url = url
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
createWindow(options, data, html) {
|
||||
|
||||
if(!(options.className in app.LoadedClasses)){
|
||||
console.error(`Missing view ${options.className} !\nLoad explicitely it from your controller, or as set it as a dependency...`);
|
||||
return null;
|
||||
}
|
||||
|
||||
let view = new app.LoadedClasses[options.className]();
|
||||
view._className = options.name.replace('.html', '');
|
||||
view._controller = this;
|
||||
|
||||
Controller._contents.push({
|
||||
view: view,
|
||||
type: 'window',
|
||||
expanded: options.expanded || false,
|
||||
visible: true,
|
||||
active: false
|
||||
});
|
||||
|
||||
let content = ui.create(`<div class="window">
|
||||
<header class="cols-2 right">
|
||||
<h1>${options.title || ''}</h1>
|
||||
<div class="controls">
|
||||
<button eicbutton data-id="${view._sparcId}" basic primary rounded xsmall class="icon-copy shrink" title="shrink"></button>
|
||||
<button eicbutton data-id="${view._sparcId}" basic primary rounded xsmall class="icon-square-o expand" title="expand"></button>
|
||||
<button eicbutton data-id="${view._sparcId}" basic primary rounded xsmall class="icon-cancel close" title="close"></button>
|
||||
</div>
|
||||
</header>
|
||||
<section></section>
|
||||
</div>`);
|
||||
if(options.expanded) content.setAttribute('expanded','');
|
||||
|
||||
let container = content.querySelector('section');
|
||||
container.innerHTML = Controller.processTemplate(options.name, html, data);
|
||||
|
||||
view.el = content;
|
||||
|
||||
// setting up window controls
|
||||
let expand = content.querySelector('header button.expand');
|
||||
expand.addEventListener('click', view.expand.bind(view));
|
||||
|
||||
let shrink = content.querySelector('header button.shrink');
|
||||
shrink.addEventListener('click', view.shrink.bind(view));
|
||||
|
||||
let close = content.querySelector('header button.close');
|
||||
close.addEventListener('click', this.onclose.bind(this));
|
||||
|
||||
content.addEventListener('click', this.onFocusRequest.bind(this));
|
||||
content.addEventListener('expanded', this.onFocusRequest.bind(this));
|
||||
content.addEventListener('shrinked', this.onFocusRequest.bind(this));
|
||||
|
||||
content.setAttribute('sparc-id', view._sparcId);
|
||||
|
||||
let parent = Controller._template.view.find('.app-workspace');
|
||||
parent.appendChild(content);
|
||||
|
||||
this.addThesaurus(view._sparcId, options.title || 'a window')
|
||||
|
||||
view.DOMContentLoaded(data);
|
||||
view.initWindowEvents();
|
||||
this.focus(view._sparcId);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
openDialog(view) {
|
||||
|
||||
let promise = new Promise(function(resolve) {
|
||||
if(typeof view === 'string') {
|
||||
let message = view;
|
||||
view = new EICDialogContent();
|
||||
view.el = message;
|
||||
}
|
||||
|
||||
let dialog = EICController.createDialog(view);
|
||||
|
||||
function commit(result) {
|
||||
EICController.closeDialog(view._sparcId);
|
||||
resolve(result);
|
||||
}
|
||||
|
||||
function abort(result) {
|
||||
EICController.closeDialog(view._sparcId);
|
||||
resolve(result);
|
||||
}
|
||||
|
||||
view.commit = commit;
|
||||
view.abort = abort;
|
||||
view.buttons.forEach(function(button) {
|
||||
dialog.querySelector('[eicdialog] > [eiccard] > footer').append(button.el);
|
||||
});
|
||||
|
||||
view.DOMContentFocused();
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by a view that needs to change the current URL
|
||||
* Typically happens when the view allows you to switch to another instance of the business-object
|
||||
* handled by this controiller. Doinbg a history.replaceState in the view is not enough, because
|
||||
* the controller needs to keep track of his current URL (in view2url) for changing
|
||||
* the Browser URL bar when -later- changing window focus.
|
||||
* @param {string} newUrl
|
||||
*/
|
||||
changeUrl(view, newUrl) {
|
||||
history.replaceState(null, null, newUrl)
|
||||
this.view2url[view._sparcId] = newUrl
|
||||
view._url = newUrl
|
||||
}
|
||||
|
||||
static createDialog(dialog, options) {
|
||||
|
||||
options = options || {};
|
||||
|
||||
let container = ui.create(`<div eicdialog sparc-id="${dialog._sparcId}">
|
||||
<article eiccard ${ options.closable || ''}>
|
||||
${ dialog.options.title ? `<header><h1>${dialog.options.title}</h1>${dialog.options.subtitle ? `<h2>${dialog.options.subtitle}</h2>`: '' }</header>`: ''}
|
||||
<section></section>
|
||||
<footer></footer>
|
||||
</article>
|
||||
</div>`);
|
||||
|
||||
ui.eicfy(container);
|
||||
|
||||
container.querySelector('[eiccard] section').append(dialog.el);
|
||||
dialog.parentContainer = container;
|
||||
|
||||
document.body.append(container)
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
static closeDialog(id) { document.body.querySelector(`[sparc-id="${id}"]`).remove(); }
|
||||
|
||||
addThesaurus(id, title) {
|
||||
let container = Controller._template.view.find('.app-content-thesaurus');
|
||||
container.querySelectorAll('[eicchip]').forEach(el => el.setAttribute('secondary', ''))
|
||||
|
||||
let chip = new Chip(null, {label: title, destroyable: true});
|
||||
chip.el.setAttribute('data-id', id);
|
||||
chip.addEventListener('click', this.onSelectThesaurus.bind(this));
|
||||
chip.addEventListener('destroy', this.onRemoveThesaurus.bind(this));
|
||||
|
||||
container.append(chip.el);
|
||||
}
|
||||
|
||||
removeThesaurus(id) {
|
||||
let container = Controller._template.view.find('.app-content-thesaurus');
|
||||
|
||||
let chip = container.querySelector(`[data-id="${id}"]`)
|
||||
if(chip) chip.remove();
|
||||
|
||||
let remaining = container.querySelectorAll('[eicchip]');
|
||||
|
||||
if(remaining.length > 0) {
|
||||
remaining.item(remaining.length - 1).dispatchEvent(new MouseEvent('click'));
|
||||
}
|
||||
}
|
||||
|
||||
close(id) {
|
||||
this.removeThesaurus(id);
|
||||
this.unloadView(Controller.getContentById(id));
|
||||
if(this.view2url.hasOwnProperty(id)) delete(this.view2url[id]);
|
||||
}
|
||||
|
||||
focus(id, data) {
|
||||
this.selectThesaurus(id);
|
||||
let curUrl = new URL(document.location.href).pathname
|
||||
if(this.view2url.hasOwnProperty(id)) {
|
||||
history.replaceState(null, null, this.view2url[id]);
|
||||
curUrl = this.view2url[id]
|
||||
}
|
||||
|
||||
if(app.matomo) app.matomo.logAction('Focus window / '+curUrl)
|
||||
|
||||
let next = Controller.getContentById(id);
|
||||
|
||||
if(Controller._currentContent) {
|
||||
if(next.expanded) {
|
||||
this.blur(Controller._currentContent);
|
||||
Controller._contents.filter(item => item.type == 'window').forEach(item => this.desactivate(item));
|
||||
Controller._contents.filter(item => !item.expanded).forEach(item => this.blur(item));
|
||||
} else {
|
||||
Controller._contents.filter(item => !item.expanded && item.type == 'window').forEach(item => this.attach(item));
|
||||
this.attach(Controller._currentContent);
|
||||
|
||||
let altexpand = Controller._contents.filter(item => item.expanded && item.visible && item.type == 'window');
|
||||
|
||||
if(altexpand.length == 0) {
|
||||
let latest = Controller._contents.reverse().find(item => item.expanded);
|
||||
if(latest) { this.attach(latest); }
|
||||
}
|
||||
}
|
||||
}
|
||||
Controller._currentContent = next;
|
||||
this.activate(Controller._currentContent, data);
|
||||
Controller._currentContent.view.DOMContentResized();
|
||||
}
|
||||
|
||||
desactivate(content) {
|
||||
content.view.el.classList.remove('active');
|
||||
content.active = false;
|
||||
if(content.expanded) this.blur(content);
|
||||
}
|
||||
|
||||
activate(content, data) {
|
||||
Controller._contents.forEach(o => o.active = false);
|
||||
Controller._contents.forEach(o => o.view.el.classList.remove('active'));
|
||||
content.view.el.classList.add('active');
|
||||
content.active = true;
|
||||
|
||||
if(!content.visible) this.attach(content);
|
||||
|
||||
content.view.DOMContentFocused(data);
|
||||
}
|
||||
|
||||
attach(content) {
|
||||
ui.show(content.view.el,!content.expanded ? 'grid': 'block');
|
||||
content.visible = true;
|
||||
}
|
||||
|
||||
blur(content) {
|
||||
ui.hide(content.view.el);
|
||||
content.visible = false;
|
||||
content.active = false;
|
||||
content.view.DOMContentBlured();
|
||||
}
|
||||
|
||||
onclose(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
this.close(event.currentTarget.dataset.id)
|
||||
}
|
||||
|
||||
onSelectThesaurus(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
this.focus(event.currentTarget.dataset.id);
|
||||
}
|
||||
|
||||
selectThesaurus(id) {
|
||||
let container = Controller._template.view.find('.app-content-thesaurus');
|
||||
container.querySelectorAll('[eicchip]').forEach(el => el.setAttribute('secondary', ''))
|
||||
container.querySelector(`[eicchip][data-id="${id}"]`).removeAttribute('secondary');
|
||||
}
|
||||
onRemoveThesaurus(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
this.close(event.currentTarget.dataset.id);
|
||||
}
|
||||
|
||||
onFocusRequest(event) {
|
||||
|
||||
let view = event.currentTarget;
|
||||
while(!view.hasAttribute('sparc-id') && !view.classList.contains('window')) view = view.parentElement;
|
||||
let content = Controller.getContentById(view.getAttribute('sparc-id'));
|
||||
if(!content.active || content.expanded != content.view.expanded || (Controller._currentContent.view._sparcId != content.view._sparcId)) {
|
||||
content.expanded = content.view.expanded;
|
||||
this.focus(view.getAttribute('sparc-id'));
|
||||
}
|
||||
}
|
||||
|
||||
static resize() {
|
||||
Controller._contents.forEach(item => item.view.DOMContentResized())
|
||||
}
|
||||
}
|
||||
|
||||
app.registerClass('EICController', EICController);
|
||||
Reference in New Issue
Block a user