Add modal manager

This commit is contained in:
Samuel Elliott 2018-02-13 16:44:07 +00:00
parent cbc727f951
commit 64ac3677b1
7 changed files with 186 additions and 43 deletions

View File

@ -13,6 +13,7 @@ import { FileUtils, ClientLogger as Logger } from 'common';
import path from 'path';
import { Events } from 'modules';
import { Error } from 'structs';
import { Modals } from 'ui';
export default class {
@ -48,8 +49,8 @@ export default class {
}
if (this.errors.length) {
Events.emit('bd-error', {
header: `${this.moduleName} - one or more ${this.contentType}(s) failed to load`,
Modals.error({
header: `${this.moduleName} - ${this.errors.length} ${this.contentType}${this.errors.length !== 1 ? 's' : ''} failed to load`,
module: this.moduleName,
type: 'err',
content: this.errors

View File

@ -16,6 +16,19 @@
}
}
.bd-modal-wrap {
transition: all 0.2s ease;
width: 100%;
height: 100%;
position: absolute;
z-index: 9000;
.bd-modal-close-area {
width: 100%;
height: 100%;
}
}
.bd-modal {
position: fixed;
align-content: space-around;

View File

@ -8,66 +8,50 @@
* LICENSE file in the root directory of this source tree.
*/
<template>
<div class="bd-modals-container">
<div v-for="(modal, index) in modals" :key="`bd-modal-${index}`">
<div v-if="index === 0" class="bd-backdrop" @click="closeModal(index)"></div>
<div v-else :style="{opacity: 0}" class="bd-backdrop" @click="closeModal(index)"></div>
<Modal :headerText="modal.header"
:close="() => closeModal(index)"
:class="[{'bd-err': modal.type && modal.type === 'err'}, {'bd-modal-out': modal.closing}]">
<MiError v-if="modal.type === 'err'" slot="icon" size="20"/>
<div slot="body">
<div v-for="(content, index) in modal.content">
<ErrorModal v-if="content._type === 'err'" :content="content" :hideStack="hideStack" :showStack="showStack"/>
</div>
</div>
<div slot="footer" class="bd-modal-controls">
<span class="bd-modal-tip">Ctrl+Shift+I for more details</span>
<div class="bd-button bd-ok" @click="closeModal(index)">
OK
</div>
</div>
</Modal>
<div v-for="(modal, index) in modals.stack" :key="`bd-modal-${index}`">
<div class="bd-backdrop" :class="{'bd-backdrop-out': modal.closing}" :style="{opacity: index === 0 ? undefined : 0}"></div>
<div class="bd-modal-wrap" :style="{transform: `scale(${downscale(index + 1)})`, opacity: downscale(index + 1)}">
<div class="bd-modal-close-area" @click="closeModal(modal)"></div>
<component :is="modal.component" />
</div>
</div>
</div>
</template>
<script>
// Imports
import { Events } from 'modules';
import { Modals } from 'ui';
import { Modal } from '../common';
import { MiError } from '../common/MaterialIcon';
import ErrorModal from '../common/ErrorModal.vue';
import ErrorModal from './modals/ErrorModal.vue';
export default {
components: {
Modal, MiError
},
data() {
return {
modals: []
}
modals: Modals,
eventListener: null
};
},
components: {
Modal, MiError, ErrorModal
},
beforeMount() {
Events.on('bd-error', e => {
e.closing = false;
this.modals.push(e);
console.log(this.modals);
created() {
console.log(this);
Events.on('bd-refresh-modals', this.eventListener = () => {
this.$forceUpdate();
});
},
destroyed() {
if (this.eventListener) Events.off('bd-refresh-modals', this.eventListener);
},
methods: {
closeModal(index) {
this.modals[index].closing = true;
setTimeout(() => {
this.modals.splice(index, 1);
}, 200);
closeModal(modal) {
modal.close();
},
showStack(error) {
error.showStack = true;
},
hideStack(error) {
error.showStack = false;
downscale(index) {
return 1 - ((this.modals.stack.filter(m => !m.closing).length - index) * 0.2);
}
}
}

View File

@ -0,0 +1,31 @@
/**
* BetterDiscord Basic Modal Component
* Copyright (c) 2015-present Jiiks/JsSucks - https://github.com/Jiiks / https://github.com/JsSucks
* All rights reserved.
* https://betterdiscord.net
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
<template>
<Modal :class="{'bd-modal-out': modal.closing}" :headerText="modal.title" :close="modal.close">
<div slot="body">{{ modal.text }}</div>
<div slot="footer" class="bd-modal-controls">
<div class="bd-flex-grow"></div>
<div class="bd-button bd-ok" @click="modal.close">OK</div>
</div>
</Modal>
</template>
<script>
// Imports
import { Modal } from '../../common';
export default {
props: ['modal'],
components: {
Modal
}
}
</script>

View File

@ -0,0 +1,47 @@
<template>
<Modal :headerText="modal.event.header" :close="modal.close"
:class="[{'bd-err': modal.event.type && modal.event.type === 'err'}, {'bd-modal-out': modal.closing}]">
<MiError v-if="modal.event.type === 'err'" slot="icon" size="20"/>
<div slot="body">
<div v-for="(content, index) in modal.event.content">
<div v-if="content._type === 'err'" class="bd-modal-error" :class="{'bd-open': content.showStack}">
<div class="bd-modal-error-title bd-flex">
<span class="bd-modal-title-text bd-flex-grow">{{content.message}}</span>
<span class="bd-modal-titlelink" v-if="content.showStack" @click="() => { content.showStack = false; $forceUpdate(); }">Hide Stacktrace</span>
<span class="bd-modal-titlelink" v-else @click="() => { content.showStack = true; $forceUpdate(); }">Show Stacktrace</span>
</div>
<div class="bd-scroller-wrap">
<div class="bd-scroller">
<div class="bd-modal-error-body"><span>{{content.err.message}}</span>
{{content.stackTrace}}</div>
</div>
</div>
</div>
</div>
</div>
<div slot="footer" class="bd-modal-controls">
<span class="bd-modal-tip">Press {{ platformInspectorKey }} for more details</span>
<div class="bd-button bd-ok" @click="modal.close">OK</div>
</div>
</Modal>
</template>
<script>
// Imports
import { Modal } from '../../common';
import { MiError } from '../../common/MaterialIcon';
const process = window.require('process');
export default {
props: ['modal'],
components: {
Modal, MiError
},
computed: {
platformInspectorKey() {
return process.platform === 'darwin' ? 'Cmd+Option+I' : 'Ctrl+Shift+I';
}
}
}
</script>

66
client/src/ui/modals.js Normal file
View File

@ -0,0 +1,66 @@
/**
* BetterDiscord Modals
* Copyright (c) 2015-present Jiiks/JsSucks - https://github.com/Jiiks / https://github.com/JsSucks
* All rights reserved.
* https://betterdiscord.net
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import { FileUtils } from 'common';
import { Events } from 'modules';
import BasicModal from './components/bd/modals/BasicModal.vue';
import ErrorModal from './components/bd/modals/ErrorModal.vue';
export default class Modals {
static add(modal, component) {
modal.component = modal.component || {
template: '<custom-modal :modal="modal" />',
components: { 'custom-modal': component },
data() { return { modal }; },
created() { modal.vue = this; }
};
modal.closing = false;
modal.close = () => this.close(modal);
this.stack.push(modal);
Events.emit('bd-refresh-modals');
return modal;
}
static close(modal) {
return new Promise((resolve, reject) => {
modal.closing = true;
setTimeout(() => {
this._stack = this.stack.filter(m => m !== modal);
Events.emit('bd-refresh-modals');
resolve();
}, 200);
});
}
static closeAll() {
for (let modal of this.stack)
modal.close();
}
static closeLast() {
if (!this.stack.length) return;
this.stack[this.stack.length - 1].close();
}
static basic(title, text) {
return this.add({ title, text }, BasicModal);
}
static error(event) {
return this.add({ event }, ErrorModal);
}
static get stack() {
return this._stack ? this._stack : (this._stack = []);
}
}

View File

@ -1,4 +1,5 @@
export { default as DOM } from './dom';
export { default as BdUI } from './bdui';
export { default as VueInjector } from './vueinjector';
export { default as Modals } from './modals';
export { default as ProfileBadges } from './profilebadges';