This commit is contained in:
syuilo 2020-09-07 14:19:53 +09:00
parent 02701d852d
commit cd79314f69
38 changed files with 240 additions and 317 deletions

View File

@ -376,7 +376,7 @@ export default defineComponent({
chooseUser() {
this.close();
const vm = os.popup(MkUserSelect, {});
const vm = os.modal(MkUserSelect, {});
vm.$once('selected', user => {
this.complete('user', user);
});

View File

@ -43,7 +43,7 @@ export default defineComponent({
icon: faCog,
text: this.$t('notificationSetting'),
action: async () => {
os.popup(await import('../notification-setting-window.vue'), {
os.modal(await import('../notification-setting-window.vue'), {
includingTypes: this.column.includingTypes,
}).$on('ok', async ({ includingTypes }) => {
this.$set(this.column, 'includingTypes', includingTypes);

View File

@ -1,46 +1,44 @@
<template>
<x-modal @closed="$emit('closed')" @click="onBgClick" :showing="showing">
<div class="mk-dialog" :class="{ iconOnly }">
<template v-if="type == 'signin'">
<mk-signin/>
</template>
<template v-else>
<div class="icon" v-if="icon">
<fa :icon="icon"/>
</div>
<div class="icon" v-else-if="!input && !select && !user" :class="type">
<fa :icon="faCheck" v-if="type === 'success'"/>
<fa :icon="faTimesCircle" v-if="type === 'error'"/>
<fa :icon="faExclamationTriangle" v-if="type === 'warning'"/>
<fa :icon="faInfoCircle" v-if="type === 'info'"/>
<fa :icon="faQuestionCircle" v-if="type === 'question'"/>
<fa :icon="faSpinner" pulse v-if="type === 'waiting'"/>
</div>
<header v-if="title" v-html="title"></header>
<header v-if="title == null && user">{{ $t('enterUsername') }}</header>
<div class="body" v-if="text" v-html="text"></div>
<mk-input v-if="input" v-model:value="inputValue" autofocus :type="input.type || 'text'" :placeholder="input.placeholder" @keydown="onInputKeydown"></mk-input>
<mk-input v-if="user" v-model:value="userInputValue" autofocus @keydown="onInputKeydown"><template #prefix>@</template></mk-input>
<mk-select v-if="select" v-model:value="selectedValue" autofocus>
<template v-if="select.items">
<option v-for="item in select.items" :value="item.value">{{ item.text }}</option>
</template>
<template v-else>
<optgroup v-for="groupedItem in select.groupedItems" :label="groupedItem.label">
<option v-for="item in groupedItem.items" :value="item.value">{{ item.text }}</option>
</optgroup>
</template>
</mk-select>
<div class="buttons" v-if="!iconOnly && (showOkButton || showCancelButton) && !actions">
<mk-button inline @click="ok" v-if="showOkButton" primary :autofocus="!input && !select && !user" :disabled="!canOk">{{ (showCancelButton || input || select || user) ? $t('ok') : $t('gotIt') }}</mk-button>
<mk-button inline @click="cancel" v-if="showCancelButton || input || select || user">{{ $t('cancel') }}</mk-button>
</div>
<div class="buttons" v-if="actions">
<mk-button v-for="action in actions" inline @click="() => { action.callback(); close(); }" :primary="action.primary" :key="action.text">{{ action.text }}</mk-button>
</div>
</template>
</div>
</x-modal>
<div class="mk-dialog" :class="{ iconOnly }">
<template v-if="type == 'signin'">
<mk-signin/>
</template>
<template v-else>
<div class="icon" v-if="icon">
<fa :icon="icon"/>
</div>
<div class="icon" v-else-if="!input && !select && !user" :class="type">
<fa :icon="faCheck" v-if="type === 'success'"/>
<fa :icon="faTimesCircle" v-if="type === 'error'"/>
<fa :icon="faExclamationTriangle" v-if="type === 'warning'"/>
<fa :icon="faInfoCircle" v-if="type === 'info'"/>
<fa :icon="faQuestionCircle" v-if="type === 'question'"/>
<fa :icon="faSpinner" pulse v-if="type === 'waiting'"/>
</div>
<header v-if="title" v-html="title"></header>
<header v-if="title == null && user">{{ $t('enterUsername') }}</header>
<div class="body" v-if="text" v-html="text"></div>
<mk-input v-if="input" v-model:value="inputValue" autofocus :type="input.type || 'text'" :placeholder="input.placeholder" @keydown="onInputKeydown"></mk-input>
<mk-input v-if="user" v-model:value="userInputValue" autofocus @keydown="onInputKeydown"><template #prefix>@</template></mk-input>
<mk-select v-if="select" v-model:value="selectedValue" autofocus>
<template v-if="select.items">
<option v-for="item in select.items" :value="item.value">{{ item.text }}</option>
</template>
<template v-else>
<optgroup v-for="groupedItem in select.groupedItems" :label="groupedItem.label">
<option v-for="item in groupedItem.items" :value="item.value">{{ item.text }}</option>
</optgroup>
</template>
</mk-select>
<div class="buttons" v-if="!iconOnly && (showOkButton || showCancelButton) && !actions">
<mk-button inline @click="ok" v-if="showOkButton" primary :autofocus="!input && !select && !user" :disabled="!canOk">{{ (showCancelButton || input || select || user) ? $t('ok') : $t('gotIt') }}</mk-button>
<mk-button inline @click="cancel" v-if="showCancelButton || input || select || user">{{ $t('cancel') }}</mk-button>
</div>
<div class="buttons" v-if="actions">
<mk-button v-for="action in actions" inline @click="() => { action.callback(); close(); }" :primary="action.primary" :key="action.text">{{ action.text }}</mk-button>
</div>
</template>
</div>
</template>
<script lang="ts">
@ -52,12 +50,10 @@ import MkInput from './ui/input.vue';
import MkSelect from './ui/select.vue';
import MkSignin from './signin.vue';
import parseAcct from '../../misc/acct/parse';
import XModal from './modal.vue';
import * as os from '@/os';
export default defineComponent({
components: {
XModal,
MkButton,
MkInput,
MkSelect,
@ -65,9 +61,6 @@ export default defineComponent({
},
props: {
showing: {
required: true
},
type: {
type: String,
required: false,
@ -118,7 +111,7 @@ export default defineComponent({
},
},
emits: ['done', 'closed'],
emits: ['done'],
data() {
return {

View File

@ -72,7 +72,7 @@ export default defineComponent({
if (this.$store.state.device.imageNewTab) {
window.open(this.image.url, '_blank');
} else {
const viewer = os.popup(ImageViewer, {
const viewer = os.modal(ImageViewer, {
image: this.image
});
this.$once('hook:beforeDestroy', () => {

View File

@ -1,55 +1,43 @@
<template>
<x-modal :source="source" :no-center="noCenter" @click="close()" @closed="$emit('closed')" :showing="showing">
<div class="rrevdjwt" :class="{ left: align === 'left' }" ref="items" :style="{ width: width + 'px' }">
<template v-for="(item, i) in items.filter(item => item !== undefined)">
<div v-if="item === null" class="divider" :key="i"></div>
<span v-else-if="item.type === 'label'" class="label item" :key="i">
<span>{{ item.text }}</span>
</span>
<router-link v-else-if="item.type === 'link'" :to="item.to" @click.native="close()" :tabindex="i" class="_button item" :key="i">
<fa v-if="item.icon" :icon="item.icon" fixed-width/>
<mk-avatar v-if="item.avatar" :user="item.avatar" class="avatar"/>
<span>{{ item.text }}</span>
<i v-if="item.indicate"><fa :icon="faCircle"/></i>
</router-link>
<a v-else-if="item.type === 'a'" :href="item.href" :target="item.target" :download="item.download" @click="close()" :tabindex="i" class="_button item" :key="i">
<fa v-if="item.icon" :icon="item.icon" fixed-width/>
<span>{{ item.text }}</span>
<i v-if="item.indicate"><fa :icon="faCircle"/></i>
</a>
<button v-else-if="item.type === 'user'" @click="clicked(item.action)" :tabindex="i" class="_button item" :key="i">
<mk-avatar :user="item.user" class="avatar"/><mk-user-name :user="item.user"/>
<i v-if="item.indicate"><fa :icon="faCircle"/></i>
</button>
<button v-else @click="clicked(item.action)" :tabindex="i" class="_button item" :key="i">
<fa v-if="item.icon" :icon="item.icon" fixed-width/>
<mk-avatar v-if="item.avatar" :user="item.avatar" class="avatar"/>
<span>{{ item.text }}</span>
<i v-if="item.indicate"><fa :icon="faCircle"/></i>
</button>
</template>
</div>
</x-modal>
<div class="rrevdjwt" :class="{ left: align === 'left' }" ref="items" :style="{ width: width + 'px' }">
<template v-for="(item, i) in items.filter(item => item !== undefined)">
<div v-if="item === null" class="divider" :key="i"></div>
<span v-else-if="item.type === 'label'" class="label item" :key="i">
<span>{{ item.text }}</span>
</span>
<router-link v-else-if="item.type === 'link'" :to="item.to" @click.native="close()" :tabindex="i" class="_button item" :key="i">
<fa v-if="item.icon" :icon="item.icon" fixed-width/>
<mk-avatar v-if="item.avatar" :user="item.avatar" class="avatar"/>
<span>{{ item.text }}</span>
<i v-if="item.indicate"><fa :icon="faCircle"/></i>
</router-link>
<a v-else-if="item.type === 'a'" :href="item.href" :target="item.target" :download="item.download" @click="close()" :tabindex="i" class="_button item" :key="i">
<fa v-if="item.icon" :icon="item.icon" fixed-width/>
<span>{{ item.text }}</span>
<i v-if="item.indicate"><fa :icon="faCircle"/></i>
</a>
<button v-else-if="item.type === 'user'" @click="clicked(item.action)" :tabindex="i" class="_button item" :key="i">
<mk-avatar :user="item.user" class="avatar"/><mk-user-name :user="item.user"/>
<i v-if="item.indicate"><fa :icon="faCircle"/></i>
</button>
<button v-else @click="clicked(item.action)" :tabindex="i" class="_button item" :key="i">
<fa v-if="item.icon" :icon="item.icon" fixed-width/>
<mk-avatar v-if="item.avatar" :user="item.avatar" class="avatar"/>
<span>{{ item.text }}</span>
<i v-if="item.indicate"><fa :icon="faCircle"/></i>
</button>
</template>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { faCircle } from '@fortawesome/free-solid-svg-icons';
import XModal from './modal.vue';
import { focusPrev, focusNext } from '@/scripts/focus';
import * as os from '@/os';
export default defineComponent({
components: {
XModal
},
props: {
showing: {
required: true
},
source: {
required: true
},
items: {
type: Array,
required: true

View File

@ -18,6 +18,9 @@ import * as os from '@/os';
// memo: popup.vuefixedsource辿
export default defineComponent({
provide: {
modal: true
},
props: {
showing: {
type: Boolean,
@ -59,58 +62,60 @@ export default defineComponent({
const popover = this.$refs.content as any;
const rect = this.source.getBoundingClientRect();
const width = popover.offsetWidth;
const height = popover.offsetHeight;
new ResizeObserver((entries, observer) => {
const rect = this.source.getBoundingClientRect();
const width = popover.offsetWidth;
const height = popover.offsetHeight;
let left;
let top;
let left;
let top;
if (os.isMobile && !this.noCenter) {
const x = rect.left + (this.fixed ? 0 : window.pageXOffset) + (this.source.offsetWidth / 2);
const y = rect.top + (this.fixed ? 0 : window.pageYOffset) + (this.source.offsetHeight / 2);
left = (x - (width / 2));
top = (y - (height / 2));
popover.style.transformOrigin = 'center';
} else {
const x = rect.left + (this.fixed ? 0 : window.pageXOffset) + (this.source.offsetWidth / 2);
const y = rect.top + (this.fixed ? 0 : window.pageYOffset) + this.source.offsetHeight;
left = (x - (width / 2));
top = y;
}
if (this.fixed) {
if (left + width > window.innerWidth) {
left = window.innerWidth - width;
if (os.isMobile && !this.noCenter) {
const x = rect.left + (this.fixed ? 0 : window.pageXOffset) + (this.source.offsetWidth / 2);
const y = rect.top + (this.fixed ? 0 : window.pageYOffset) + (this.source.offsetHeight / 2);
left = (x - (width / 2));
top = (y - (height / 2));
popover.style.transformOrigin = 'center';
} else {
const x = rect.left + (this.fixed ? 0 : window.pageXOffset) + (this.source.offsetWidth / 2);
const y = rect.top + (this.fixed ? 0 : window.pageYOffset) + this.source.offsetHeight;
left = (x - (width / 2));
top = y;
}
if (top + height > window.innerHeight) {
top = window.innerHeight - height;
popover.style.transformOrigin = 'center';
}
} else {
if (left + width - window.pageXOffset > window.innerWidth) {
left = window.innerWidth - width + window.pageXOffset;
popover.style.transformOrigin = 'center';
if (this.fixed) {
if (left + width > window.innerWidth) {
left = window.innerWidth - width;
popover.style.transformOrigin = 'center';
}
if (top + height > window.innerHeight) {
top = window.innerHeight - height;
popover.style.transformOrigin = 'center';
}
} else {
if (left + width - window.pageXOffset > window.innerWidth) {
left = window.innerWidth - width + window.pageXOffset;
popover.style.transformOrigin = 'center';
}
if (top + height - window.pageYOffset > window.innerHeight) {
top = window.innerHeight - height + window.pageYOffset;
popover.style.transformOrigin = 'center';
}
}
if (top + height - window.pageYOffset > window.innerHeight) {
top = window.innerHeight - height + window.pageYOffset;
popover.style.transformOrigin = 'center';
if (top < 0) {
top = 0;
}
}
if (top < 0) {
top = 0;
}
if (left < 0) {
left = 0;
}
if (left < 0) {
left = 0;
}
popover.style.left = left + 'px';
popover.style.top = top + 'px';
popover.style.left = left + 'px';
popover.style.top = top + 'px';
}).observe(popover);
});
},
});

View File

@ -96,7 +96,7 @@
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { defineAsyncComponent, defineComponent } from 'vue';
import { faSatelliteDish, faBolt, faTimes, faBullhorn, faStar, faLink, faExternalLinkSquareAlt, faPlus, faMinus, faRetweet, faReply, faReplyAll, faEllipsisH, faHome, faUnlock, faEnvelope, faThumbtack, faBan, faQuoteRight, faInfoCircle, faBiohazard, faPlug } from '@fortawesome/free-solid-svg-icons';
import { faCopy, faTrashAlt, faEdit, faEye, faEyeSlash } from '@fortawesome/free-regular-svg-icons';
import { parse } from '../../mfm/parse';
@ -108,8 +108,6 @@ import XReactionsViewer from './reactions-viewer.vue';
import XMediaList from './media-list.vue';
import XCwButton from './cw-button.vue';
import XPoll from './poll.vue';
import MkUrlPreview from './url-preview.vue';
import MkReactionPicker from './reaction-picker.vue';
import { pleaseLogin } from '@/scripts/please-login';
import { focusPrev, focusNext } from '@/scripts/focus';
import { url } from '@/config';
@ -133,7 +131,7 @@ export default defineComponent({
XMediaList,
XCwButton,
XPoll,
MkUrlPreview,
MkUrlPreview: defineAsyncComponent(() => import('@/components/url-preview.vue')),
},
inject: {
@ -485,17 +483,17 @@ export default defineComponent({
react(viaKeyboard = false) {
pleaseLogin();
this.blur();
const close = os.popup(MkReactionPicker, {
source: this.$refs.reactButton,
os.modal(defineAsyncComponent(() => import('@/components/reaction-picker.vue')), {
showFocus: viaKeyboard,
}, reaction => {
os.api('notes/reactions/create', {
noteId: this.appearNote.id,
reaction: reaction
}).then(() => {
close();
});
}, this.focus);
this.focus();
}, {
source: this.$refs.reactButton
});
},
reactDirectly(reaction) {

View File

@ -32,12 +32,11 @@ import { defineComponent } from 'vue';
import paging from '@/scripts/paging';
import XNote from './note.vue';
import XList from './date-separated-list.vue';
import MkButton from './ui/button.vue';
import * as os from '@/os';
export default defineComponent({
components: {
XNote, XList, MkButton
XNote, XList,
},
mixins: [
@ -83,9 +82,9 @@ export default defineComponent({
updated(oldValue, newValue) {
const i = this.notes.findIndex(n => n === oldValue);
if (this.prop) {
Vue.set(this.items[i], this.prop, newValue);
this.items[i][this.prop] = newValue;
} else {
Vue.set(this.items, i, newValue);
this.items[i] = newValue;
}
},

View File

@ -57,9 +57,6 @@ import * as os from '@/os';
export default defineComponent({
props: {
destroy: {
required: true
},
x: {
type: Number,
required: true
@ -94,7 +91,7 @@ export default defineComponent({
},
mounted() {
setTimeout(() => {
this.destroy();
this.$emit('closed');
}, 1100);
}
});

View File

@ -1,94 +0,0 @@
<template>
<x-modal @closed="$emit('closed')" @click="onBgClick" :showing="showing">
<x-post-form ref="form" class="ulveipgl"
:reply="reply"
:renote="renote"
:mention="mention"
:specified="specified"
:initial-text="initialText"
:initial-note="initialNote"
:instant="instant"
@posted="onPosted"
@cancel="onCanceled"
/>
</x-modal>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import XModal from './modal.vue';
import XPostForm from './post-form.vue';
import * as os from '@/os';
export default defineComponent({
components: {
XModal,
XPostForm,
},
props: {
showing: {
required: true
},
reply: {
type: Object,
required: false
},
renote: {
type: Object,
required: false
},
mention: {
type: Object,
required: false
},
specified: {
type: Object,
required: false
},
initialText: {
type: String,
required: false
},
initialNote: {
type: Object,
required: false
},
instant: {
type: Boolean,
required: false,
default: false
}
},
methods: {
focus() {
this.$refs.form.focus();
},
onPosted() {
this.$emit('done', 'posted');
},
onCanceled() {
this.$emit('done', 'canceled');
},
onKeydown(e) {
if (e.which === 27) { // Esc
e.preventDefault();
e.stopPropagation();
this.$emit('done', 'canceled');
}
},
}
});
</script>
<style lang="scss" scoped>
.ulveipgl {
width: 100%;
max-width: 500px;
border-radius: var(--radius);
}
</style>

View File

@ -1,5 +1,5 @@
<template>
<div class="gafaadew"
<div class="gafaadew" :class="{ modal }"
@dragover.stop="onDragover"
@dragenter="onDragenter"
@dragleave="onDragleave"
@ -80,6 +80,8 @@ export default defineComponent({
XPollEditor: defineAsyncComponent(() => import('./poll-editor.vue'))
},
inject: ['modal'],
props: {
reply: {
type: Object,
@ -417,7 +419,7 @@ export default defineComponent({
// TODO: information dialog
return;
}
const w = os.popup(MkVisibilityChooser, {
const w = os.modal(MkVisibilityChooser, {
source: this.$refs.visibilityButton,
currentVisibility: this.visibility,
currentLocalOnly: this.localOnly
@ -433,7 +435,7 @@ export default defineComponent({
},
addVisibleUser() {
const vm = os.popup(MkUserSelect, {});
const vm = os.modal(MkUserSelect, {});
vm.$once('selected', user => {
this.visibleUsers.push(user);
});
@ -593,18 +595,18 @@ export default defineComponent({
},
cancel() {
this.$emit('cancel');
this.$emit('done');
},
insertMention() {
const vm = os.popup(MkUserSelect, {});
const vm = os.modal(MkUserSelect, {});
vm.$once('selected', user => {
insertTextAtCursor(this.$refs.text, getAcct(user) + ' ');
});
},
async insertEmoji(ev) {
const vm = os.popup(await import('./emoji-picker.vue'), {
const vm = os.modal(await import('./emoji-picker.vue'), {
source: ev.currentTarget || ev.target
}).$once('chosen', emoji => {
insertTextAtCursor(this.$refs.text, emoji);
@ -636,6 +638,12 @@ export default defineComponent({
position: relative;
background: var(--panel);
&.modal {
width: 100%;
max-width: 500px;
border-radius: var(--radius);
}
> header {
z-index: 1000;
height: 66px;

View File

@ -1,36 +1,24 @@
<template>
<XModal :source="source" @closed="$emit('closed')" :showing="showing" @click="close" v-hotkey.global="keymap">
<div class="rdfaahpb">
<div class="buttons" ref="buttons" :class="{ showFocus }">
<button class="_button" v-for="(reaction, i) in rs" :key="reaction" @click="react(reaction)" :tabindex="i + 1" :title="reaction" v-particle><x-reaction-icon :reaction="reaction"/></button>
</div>
<input class="text" v-model.trim="text" :placeholder="$t('enterEmoji')" @keyup.enter="reactText" @input="tryReactText" v-autocomplete="{ model: 'text' }">
<div class="rdfaahpb">
<div class="buttons" ref="buttons" :class="{ showFocus }">
<button class="_button" v-for="(reaction, i) in rs" :key="reaction" @click="react(reaction)" :tabindex="i + 1" :title="reaction" v-particle><x-reaction-icon :reaction="reaction"/></button>
</div>
</XModal>
<input class="text" v-model.trim="text" :placeholder="$t('enterEmoji')" @keyup.enter="reactText" @input="tryReactText" v-autocomplete="{ model: 'text' }">
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { emojiRegex } from '../../misc/emoji-regex';
import XReactionIcon from './reaction-icon.vue';
import XModal from './modal.vue';
import * as os from '@/os';
export default defineComponent({
components: {
XModal,
XReactionIcon,
},
props: {
showing: {
required: true
},
source: {
required: true
},
reactions: {
required: false
},

View File

@ -111,7 +111,7 @@ export default defineComponent({
this.closeDetails();
if (!this.isHovering) return;
this.details = os.popup(XDetails, {
this.details = os.modal(XDetails, {
reaction: this.reaction,
users,
count: this.count,

View File

@ -276,7 +276,7 @@ export default defineComponent({
},
async addAcount() {
os.popup(await import('./signin-dialog.vue')).$once('login', res => {
os.modal(await import('./signin-dialog.vue')).$once('login', res => {
this.$store.dispatch('addAcount', res);
os.dialog({
type: 'success',
@ -286,7 +286,7 @@ export default defineComponent({
},
async createAccount() {
os.popup(await import('./signup-dialog.vue')).$once('signup', res => {
os.modal(await import('./signup-dialog.vue')).$once('signup', res => {
this.$store.dispatch('addAcount', res);
this.switchAccountWithToken(res.i);
});

View File

@ -166,7 +166,7 @@ export default defineComponent({
id: notification.id
});
os.popup(await import('@/components/toast.vue'), {
os.modal(await import('@/components/toast.vue'), {
notification
});
}

View File

@ -335,7 +335,7 @@ export default defineComponent({
id: notification.id
});
os.popup(await import('@/components/toast.vue'), {
os.modal(await import('@/components/toast.vue'), {
notification
});
}

View File

@ -1,4 +1,4 @@
import { Component, defineAsyncComponent, ref } from 'vue';
import { Component, defineAsyncComponent, markRaw, ref } from 'vue';
import Stream from '@/scripts/stream';
import { store } from '@/store';
import { apiUrl } from '@/config';
@ -44,46 +44,81 @@ export function api(endpoint: string, data: Record<string, any> = {}, token?: st
return promise;
}
export function popup(component: Component, props: Record<string, any>, callback?: Function) {
export function popup(component: Component, props: Record<string, any>, callback?: Function, option?) {
markRaw(component);
const id = Math.random().toString(); // TODO: uuidとか使う
const showing = ref(true);
const popup = {
const close = (...args) => {
if (callback) callback(...args);
showing.value = false;
};
const modal = {
type: 'popup',
component,
props: {
...props,
showing
},
props,
showing,
done: (...args) => {
if (callback) callback(...args);
showing.value = false;
},
source: option?.source,
done: close,
bgClick: () => close(),
closed: () => {
store.commit('removePopup', id);
},
id,
};
store.commit('addPopup', popup);
return () => {
store.commit('addPopup', modal);
return close;
}
export function modal(component: Component, props: Record<string, any>, callback?: Function, option?) {
markRaw(component);
const id = Math.random().toString(); // TODO: uuidとか使う
const showing = ref(true);
const close = (...args) => {
if (callback) callback(...args);
showing.value = false;
};
const modal = {
type: 'modal',
component,
props,
showing,
source: option?.source,
done: close,
bgClick: () => close(),
closed: () => {
store.commit('removePopup', id);
},
id,
};
store.commit('addPopup', modal);
return close;
}
export function dialog(props: Record<string, any>) {
return new Promise((res, rej) => {
popup(defineAsyncComponent(() => import('@/components/dialog.vue')), props, res);
modal(defineAsyncComponent(() => import('@/components/dialog.vue')), props, result => {
if (result) {
res(result);
} else {
res({ canceled: true });
}
});
});
}
export function menu(props: Record<string, any>) {
const source = props.source; // TODO: sourceはpropsの外に出して追加の引数として受け取るようにする
return new Promise((res, rej) => {
popup(defineAsyncComponent(() => import('@/components/menu.vue')), props, res);
modal(defineAsyncComponent(() => import('@/components/menu.vue')), props, res, {
position: 'source',
source
});
});
}
export function post(props: Record<string, any>) {
return new Promise((res, rej) => {
popup(defineAsyncComponent(() => import('@/components/post-form-dialog.vue')), props, res);
modal(defineAsyncComponent(() => import('@/components/post-form.vue')), props, res);
});
}

View File

@ -54,13 +54,13 @@ export default defineComponent({
methods: {
signin() {
os.popup(XSigninDialog, {
os.modal(XSigninDialog, {
autoSet: true
});
},
signup() {
os.popup(XSignupDialog, {
os.modal(XSignupDialog, {
autoSet: true
});
}

View File

@ -125,7 +125,7 @@ export default defineComponent({
},
info(instance) {
os.popup(MkInstanceInfo, {
os.modal(MkInstanceInfo, {
instance: instance
});
}

View File

@ -550,7 +550,7 @@ export default defineComponent({
host: q
});
}
os.popup(MkInstanceInfo, {
os.modal(MkInstanceInfo, {
instance: instance
});
},

View File

@ -439,7 +439,7 @@ export default defineComponent({
},
showFollowing() {
os.popup(MkUsersDialog, {
os.modal(MkUsersDialog, {
title: this.$t('instanceFollowing'),
pagination: {
endpoint: 'federation/following',
@ -453,7 +453,7 @@ export default defineComponent({
},
showFollowers() {
os.popup(MkUsersDialog, {
os.modal(MkUsersDialog, {
title: this.$t('instanceFollowers'),
pagination: {
endpoint: 'federation/followers',
@ -467,7 +467,7 @@ export default defineComponent({
},
showUsers() {
os.popup(MkUsersDialog, {
os.modal(MkUsersDialog, {
title: this.$t('instanceUsers'),
pagination: {
endpoint: 'federation/users',

View File

@ -452,7 +452,7 @@ export default defineComponent({
},
addPinUser() {
os.popup(MkUserSelect, {}).$once('selected', user => {
os.modal(MkUserSelect, {}).$once('selected', user => {
this.pinnedUsers = this.pinnedUsers.trim();
this.pinnedUsers += '\n@' + getAcct(user);
this.pinnedUsers = this.pinnedUsers.trim();
@ -460,7 +460,7 @@ export default defineComponent({
},
chooseProxyAccount() {
os.popup(MkUserSelect, {}).$once('selected', user => {
os.modal(MkUserSelect, {}).$once('selected', user => {
this.proxyAccount = user;
this.proxyAccountId = user.id;
this.save(true);

View File

@ -180,7 +180,7 @@ export default defineComponent({
},
searchUser() {
os.popup(MkUserSelect, {}).$once('selected', user => {
os.modal(MkUserSelect, {}).$once('selected', user => {
this.show(user);
});
},

View File

@ -132,7 +132,7 @@ export default defineComponent({
},
async startUser() {
os.popup(MkUserSelect, {}).$once('selected', user => {
os.modal(MkUserSelect, {}).$once('selected', user => {
this.$router.push(`/my/messaging/${getAcct(user)}`);
});
},

View File

@ -220,7 +220,7 @@ export default defineComponent({
},
async insertEmoji(ev) {
const vm = os.popup(await import('@/components/emoji-picker.vue'), {
const vm = os.modal(await import('@/components/emoji-picker.vue'), {
source: ev.currentTarget || ev.target
}).$once('chosen', emoji => {
insertTextAtCursor(this.$refs.text, emoji);

View File

@ -177,7 +177,7 @@ export default defineComponent({
},
addUser() {
os.popup(MkUserSelect, {}).$once('selected', user => {
os.modal(MkUserSelect, {}).$once('selected', user => {
this.users = this.users.trim();
this.users += '\n@' + getAcct(user);
this.users = this.users.trim();

View File

@ -89,7 +89,7 @@ export default defineComponent({
},
invite() {
os.popup(MkUserSelect, {}).$once('selected', user => {
os.modal(MkUserSelect, {}).$once('selected', user => {
os.api('users/groups/invite', {
groupId: this.group.id,
userId: user.id
@ -134,7 +134,7 @@ export default defineComponent({
},
transfer() {
os.popup(MkUserSelect, {}).$once('selected', user => {
os.modal(MkUserSelect, {}).$once('selected', user => {
os.api('users/groups/transfer', {
groupId: this.group.id,
userId: user.id

View File

@ -88,7 +88,7 @@ export default defineComponent({
},
addUser() {
os.popup(MkUserSelect, {}).$once('selected', user => {
os.modal(MkUserSelect, {}).$once('selected', user => {
os.api('users/lists/push', {
listId: this.list.id,
userId: user.id

View File

@ -26,7 +26,7 @@ export default defineComponent({
},
methods: {
async generateToken() {
os.popup(await import('@/components/token-generate-window.vue'), {
os.modal(await import('@/components/token-generate-window.vue'), {
}).$on('ok', async ({ name, permissions }) => {
const { token } = await os.api('miauth/gen-token', {
session: null,

View File

@ -114,7 +114,7 @@ export default defineComponent({
},
async configure() {
os.popup(await import('@/components/notification-setting-window.vue'), {
os.modal(await import('@/components/notification-setting-window.vue'), {
includingTypes: this.$store.state.i.includingNotificationTypes,
showGlobalToggle: false,
}).$on('ok', async ({ includingTypes: value }: any) => {

View File

@ -58,7 +58,7 @@ export default defineComponent({
},
preview(ev) {
const picker = os.popup(MkReactionPicker, {
const picker = os.modal(MkReactionPicker, {
source: ev.currentTarget || ev.target,
reactions: this.splited,
showFocus: false,
@ -73,7 +73,7 @@ export default defineComponent({
},
async chooseEmoji(ev) {
const vm = os.popup(await import('@/components/emoji-picker.vue'), {
const vm = os.modal(await import('@/components/emoji-picker.vue'), {
source: ev.currentTarget || ev.target
}).$once('chosen', emoji => {
this.reactions += emoji;

View File

@ -118,7 +118,7 @@ export default defineComponent({
}
const token = permissions == null || permissions.length === 0 ? null : await new Promise(async (res, rej) => {
os.popup(await import('@/components/token-generate-window.vue'), {
os.modal(await import('@/components/token-generate-window.vue'), {
title: this.$t('tokenRequested'),
information: this.$t('pluginTokenRequestedDescription'),
initialName: name,

View File

@ -60,7 +60,7 @@ export default defineComponent({
if (this.title) text += `${this.title}\n`;
if (this.text) text += `${this.text}\n`;
if (this.url) text += `${this.url}`;
os.popup(PostFormDialog, {
os.modal(PostFormDialog, {
instant: true,
initialText: text.trim()
}).$once('posted', () => {

View File

@ -189,7 +189,7 @@ export default defineComponent({
},
menu() {
os.popup(XUserMenu, {
os.modal(XUserMenu, {
source: this.$refs.menu,
user: this.user
});

View File

@ -2,13 +2,17 @@
<DeckUI v-if="deckmode"/>
<DefaultUI v-else/>
<component v-for="popup in $store.state.popups" :is="popup.component" v-bind="popup.props" :key="popup.id" @done="popup.done" @closed="popup.closed"/>
<XModal v-for="modal in $store.state.popups.filter(x => x.type === 'modal')" :key="modal.id" @closed="modal.closed" @click="modal.bgClick" :showing="modal.showing" :source="modal.source">
<component :is="modal.component" v-bind="modal.props" @done="modal.done"/>
</XModal>
<component v-for="popup in $store.state.popups.filter(x => x.type === 'popup')" :key="popup.id" :is="popup.component" v-bind="popup.props" @done="popup.done" @closed="popup.closed"/>
<div id="wait" v-if="$store.state.pendingApiRequestsCount > 0"></div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { defineAsyncComponent, defineComponent } from 'vue';
import DefaultUI from './default.vue';
import DeckUI from './deck.vue';
import { instanceName, deckmode } from '@/config';
@ -17,6 +21,7 @@ export default defineComponent({
components: {
DefaultUI,
DeckUI,
XModal: defineAsyncComponent(() => import('@/components/modal.vue'))
},
metaInfo: {

View File

@ -111,6 +111,7 @@ export const store = createStore({
popups: [] as {
id: any;
component: any;
type: 'popup' | 'modal',
props: Record<string, any>;
}[],
fullView: false,

View File

@ -51,7 +51,7 @@ export default defineComponent({
methods: {
async configure() {
os.popup(await import('@/components/notification-setting-window.vue'), {
os.modal(await import('@/components/notification-setting-window.vue'), {
includingTypes: this.props.includingTypes,
}).$on('ok', async ({ includingTypes }) => {
this.props.includingTypes = includingTypes;

View File

@ -52,13 +52,13 @@ export default defineComponent({
methods: {
signin() {
os.popup(XSigninDialog, {
os.modal(XSigninDialog, {
autoSet: true
});
},
signup() {
os.popup(XSignupDialog, {
os.modal(XSignupDialog, {
autoSet: true
});
}