mirror of
https://github.com/flarum/framework.git
synced 2025-04-07 15:26:57 +08:00

While seemingly correct, an onremove method in Modal that triggers animateHide is problematic, because if one modal is opened from another, the one currently open will be removed from the DOM, triggering animateHide, and closing the new modal. To compensate, an onupdate method now closes a modal if one is open but shouldn't be; this supports the functionality of the old method when the modal is closed not from the modal instance itself (e.g. app.modal.close()) This is not ideal, but necessary. We should consider eventually expanding the modal system to support showing multiple modals at the same time (stacked over each other). Then, we can move this back to individual modals.
135 lines
2.8 KiB
JavaScript
135 lines
2.8 KiB
JavaScript
import Component from '../Component';
|
|
import Alert from './Alert';
|
|
import Button from './Button';
|
|
|
|
/**
|
|
* The `Modal` component displays a modal dialog, wrapped in a form. Subclasses
|
|
* should implement the `className`, `title`, and `content` methods.
|
|
*
|
|
* @abstract
|
|
*/
|
|
export default class Modal extends Component {
|
|
/**
|
|
* Determine whether or not the modal should be dismissible via an 'x' button.
|
|
*/
|
|
static isDismissible = true;
|
|
|
|
/**
|
|
* Attributes for an alert component to show below the header.
|
|
*
|
|
* @type {object}
|
|
*/
|
|
alertAttrs = null;
|
|
|
|
oncreate(vnode) {
|
|
super.oncreate(vnode);
|
|
|
|
this.attrs.onshow(() => this.onready());
|
|
}
|
|
|
|
view() {
|
|
if (this.alertAttrs) {
|
|
this.alertAttrs.dismissible = false;
|
|
}
|
|
|
|
return (
|
|
<div className={'Modal modal-dialog ' + this.className()}>
|
|
<div className="Modal-content">
|
|
{this.constructor.isDismissible ? (
|
|
<div className="Modal-close App-backControl">
|
|
{Button.component({
|
|
icon: 'fas fa-times',
|
|
onclick: this.hide.bind(this),
|
|
className: 'Button Button--icon Button--link',
|
|
})}
|
|
</div>
|
|
) : (
|
|
''
|
|
)}
|
|
|
|
<form onsubmit={this.onsubmit.bind(this)}>
|
|
<div className="Modal-header">
|
|
<h3 className="App-titleControl App-titleControl--text">{this.title()}</h3>
|
|
</div>
|
|
|
|
{this.alertAttrs ? <div className="Modal-alert">{Alert.component(this.alertAttrs)}</div> : ''}
|
|
|
|
{this.content()}
|
|
</form>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get the class name to apply to the modal.
|
|
*
|
|
* @return {String}
|
|
* @abstract
|
|
*/
|
|
className() {}
|
|
|
|
/**
|
|
* Get the title of the modal dialog.
|
|
*
|
|
* @return {String}
|
|
* @abstract
|
|
*/
|
|
title() {}
|
|
|
|
/**
|
|
* Get the content of the modal.
|
|
*
|
|
* @return {VirtualElement}
|
|
* @abstract
|
|
*/
|
|
content() {}
|
|
|
|
/**
|
|
* Handle the modal form's submit event.
|
|
*
|
|
* @param {Event} e
|
|
*/
|
|
onsubmit() {}
|
|
|
|
/**
|
|
* Focus on the first input when the modal is ready to be used.
|
|
*/
|
|
onready() {
|
|
this.$('form').find('input, select, textarea').first().focus().select();
|
|
}
|
|
|
|
/**
|
|
* Hide the modal.
|
|
*/
|
|
hide() {
|
|
this.attrs.onhide();
|
|
}
|
|
|
|
/**
|
|
* Stop loading.
|
|
*/
|
|
loaded() {
|
|
this.loading = false;
|
|
m.redraw();
|
|
}
|
|
|
|
/**
|
|
* Show an alert describing an error returned from the API, and give focus to
|
|
* the first relevant field.
|
|
*
|
|
* @param {RequestError} error
|
|
*/
|
|
onerror(error) {
|
|
this.alertAttrs = error.alert;
|
|
|
|
m.redraw();
|
|
|
|
if (error.status === 422 && error.response.errors) {
|
|
this.$('form [name=' + error.response.errors[0].source.pointer.replace('/data/attributes/', '') + ']').select();
|
|
} else {
|
|
this.onready();
|
|
}
|
|
}
|
|
}
|