wip
This commit is contained in:
parent
e441ee9d8a
commit
4d68baca0d
|
@ -207,7 +207,7 @@ export default defineComponent({
|
|||
},
|
||||
|
||||
showMenu() {
|
||||
this.$root.menu({
|
||||
this.$store.dispatch('showMenu', {
|
||||
items: this.getMenu(),
|
||||
source: this.$refs.menu,
|
||||
});
|
||||
|
|
|
@ -82,7 +82,7 @@ export default defineComponent({
|
|||
if (this.selectMode) {
|
||||
this.$emit('chosen', this.file);
|
||||
} else {
|
||||
this.$root.menu({
|
||||
this.$store.dispatch('showMenu', {
|
||||
items: [{
|
||||
text: this.$t('rename'),
|
||||
icon: faICursor,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<x-popup :source="source" :no-center="noCenter" :fixed="fixed" :width="width" ref="popup" @closed="() => { $emit('closed'); destroyDom(); }" v-hotkey.global="keymap">
|
||||
<div class="rrevdjwt" :class="{ left: align === 'left' }" ref="items">
|
||||
<x-modal :source="source" :no-center="noCenter" ref="popup" @click="" @closed="$store.commit('removeMenu', id)" :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">
|
||||
|
@ -29,18 +29,18 @@
|
|||
</button>
|
||||
</template>
|
||||
</div>
|
||||
</x-popup>
|
||||
</x-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { faCircle } from '@fortawesome/free-solid-svg-icons';
|
||||
import XPopup from './popup.vue';
|
||||
import XModal from './modal.vue';
|
||||
import { focusPrev, focusNext } from '../scripts/focus';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
XPopup
|
||||
XModal
|
||||
},
|
||||
props: {
|
||||
source: {
|
||||
|
@ -58,10 +58,6 @@ export default defineComponent({
|
|||
type: Boolean,
|
||||
required: false
|
||||
},
|
||||
fixed: {
|
||||
type: Boolean,
|
||||
required: false
|
||||
},
|
||||
width: {
|
||||
type: Number,
|
||||
required: false
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
// memo: popup.vueのfixedプロパティに相当するものはsource要素の祖先を辿るなどして自動で判定できるのでは
|
||||
|
||||
export default defineComponent({
|
||||
emits: ['click', 'esc', 'closed'],
|
||||
props: {
|
||||
|
@ -26,6 +28,9 @@ export default defineComponent({
|
|||
required: false,
|
||||
default: true,
|
||||
},
|
||||
sourceEl: {
|
||||
required: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
keymap(): any {
|
||||
|
@ -34,6 +39,64 @@ export default defineComponent({
|
|||
};
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
const popover = this.$refs.content as any;
|
||||
|
||||
const rect = this.source.getBoundingClientRect();
|
||||
const width = popover.offsetWidth;
|
||||
const height = popover.offsetHeight;
|
||||
|
||||
let left;
|
||||
let top;
|
||||
|
||||
if (this.$root.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;
|
||||
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 < 0) {
|
||||
top = 0;
|
||||
}
|
||||
|
||||
if (left < 0) {
|
||||
left = 0;
|
||||
}
|
||||
|
||||
popover.style.left = left + 'px';
|
||||
popover.style.top = top + 'px';
|
||||
});
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -452,7 +452,7 @@ export default defineComponent({
|
|||
renote(viaKeyboard = false) {
|
||||
pleaseLogin(this.$root);
|
||||
this.blur();
|
||||
this.$root.menu({
|
||||
this.$store.dispatch('showMenu', {
|
||||
items: [{
|
||||
text: this.$t('renote'),
|
||||
icon: faRetweet,
|
||||
|
@ -685,7 +685,7 @@ export default defineComponent({
|
|||
}))]);
|
||||
}
|
||||
|
||||
this.$root.menu({
|
||||
this.$store.dispatch('showMenu', {
|
||||
items: menu,
|
||||
source: this.$refs.menuButton,
|
||||
viaKeyboard
|
||||
|
@ -694,7 +694,7 @@ export default defineComponent({
|
|||
|
||||
showRenoteMenu(viaKeyboard = false) {
|
||||
if (!this.isMyRenote) return;
|
||||
this.$root.menu({
|
||||
this.$store.dispatch('showMenu', {
|
||||
items: [{
|
||||
text: this.$t('unrenote'),
|
||||
icon: faTrashAlt,
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
// popupはmodalと統合
|
||||
|
||||
<template>
|
||||
<div class="mk-popup" v-hotkey.global="keymap">
|
||||
<transition :name="$store.state.device.animation ? 'bg-fade' : ''" appear>
|
||||
|
|
|
@ -80,7 +80,7 @@ export default defineComponent({
|
|||
},
|
||||
showFileMenu(file, ev: MouseEvent) {
|
||||
if (this.menu) return;
|
||||
this.menu = this.$root.menu({
|
||||
this.menu = this.$store.dispatch('showMenu', {
|
||||
items: [{
|
||||
text: this.$t('renameFile'),
|
||||
icon: faICursor,
|
||||
|
|
|
@ -346,7 +346,7 @@ export default defineComponent({
|
|||
},
|
||||
|
||||
chooseFileFrom(ev) {
|
||||
this.$root.menu({
|
||||
this.$store.dispatch('showMenu', {
|
||||
items: [{
|
||||
type: 'label',
|
||||
text: this.$t('attachFile'),
|
||||
|
@ -612,7 +612,7 @@ export default defineComponent({
|
|||
},
|
||||
|
||||
showActions(ev) {
|
||||
this.$root.menu({
|
||||
this.$store.dispatch('showMenu', {
|
||||
items: this.$store.state.postFormActions.map(action => ({
|
||||
text: action.title,
|
||||
action: () => {
|
||||
|
|
|
@ -149,7 +149,7 @@ export default defineComponent({
|
|||
action: () => { this.switchAccount(account); }
|
||||
}));
|
||||
|
||||
this.$root.menu({
|
||||
this.$store.dispatch('showMenu', {
|
||||
items: [...[{
|
||||
type: 'link',
|
||||
text: this.$t('profile'),
|
||||
|
@ -164,7 +164,7 @@ export default defineComponent({
|
|||
icon: faPlus,
|
||||
text: this.$t('addAcount'),
|
||||
action: () => {
|
||||
this.$root.menu({
|
||||
this.$store.dispatch('showMenu', {
|
||||
items: [{
|
||||
text: this.$t('existingAcount'),
|
||||
action: () => { this.addAcount(); },
|
||||
|
@ -187,7 +187,7 @@ export default defineComponent({
|
|||
},
|
||||
|
||||
oepnInstanceMenu(ev) {
|
||||
this.$root.menu({
|
||||
this.$store.dispatch('showMenu', {
|
||||
items: [{
|
||||
type: 'link',
|
||||
text: this.$t('dashboard'),
|
||||
|
@ -250,7 +250,7 @@ export default defineComponent({
|
|||
action: def.action,
|
||||
indicate: def.indicated,
|
||||
}));
|
||||
this.$root.menu({
|
||||
this.$store.dispatch('showMenu', {
|
||||
items: [...items, null, {
|
||||
type: 'link',
|
||||
text: this.$t('help'),
|
||||
|
|
|
@ -39,7 +39,7 @@ export default defineComponent({
|
|||
methods: {
|
||||
menu(ev) {
|
||||
this.menuOpened = true;
|
||||
this.$root.menu({
|
||||
this.$store.dispatch('showMenu', {
|
||||
items: [{
|
||||
text: this.$t('addFile'),
|
||||
type: 'label'
|
||||
|
|
|
@ -171,7 +171,7 @@ export default defineComponent({
|
|||
this.$router.push(`/channels/${channel.id}`);
|
||||
}
|
||||
}));
|
||||
this.$root.menu({
|
||||
this.$store.dispatch('showMenu', {
|
||||
items: [{
|
||||
text: this.$t('_timelines.home'),
|
||||
icon: faHome,
|
||||
|
|
|
@ -115,7 +115,7 @@ export default defineComponent({
|
|||
},
|
||||
|
||||
start(ev) {
|
||||
this.$root.menu({
|
||||
this.$store.dispatch('showMenu', {
|
||||
items: [{
|
||||
text: this.$t('messagingWithUser'),
|
||||
icon: faUser,
|
||||
|
|
|
@ -255,7 +255,7 @@ export default defineComponent({
|
|||
|
||||
showTypeMenu(e: MouseEvent) {
|
||||
return new Promise<ThemeValue>((resolve) => {
|
||||
this.$root.menu({
|
||||
this.$store.dispatch('showMenu', {
|
||||
items: [{
|
||||
text: this.$t('_theme.defaultValue'),
|
||||
action: () => resolve(null),
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
<XPostFormDialog v-if="$store.state.postForm" v-bind="$store.state.postForm"/>
|
||||
|
||||
<XMenu v-if="menu" v-bind="menu" :key="menu.id"/>
|
||||
|
||||
<XDialog v-if="dialog" v-bind="dialog" :key="dialog.id"/>
|
||||
</template>
|
||||
|
||||
|
@ -18,6 +20,7 @@ export default defineComponent({
|
|||
DefaultUI,
|
||||
DeckUI,
|
||||
XDialog: defineAsyncComponent(() => import('./components/dialog.vue')),
|
||||
XMenu: defineAsyncComponent(() => import('./components/menu.vue')),
|
||||
XPostFormDialog: defineAsyncComponent(() => import('./components/post-form-dialog.vue')),
|
||||
},
|
||||
|
||||
|
@ -51,6 +54,12 @@ export default defineComponent({
|
|||
// what: ダイアログが複数ある場合は、一番最後に追加されたダイアログを表示する
|
||||
// why: ダイアログが一度に複数表示されるとユーザビリティが悪いため。
|
||||
return this.$store.state.dialogs[this.$store.state.dialogs.length - 1];
|
||||
},
|
||||
|
||||
menu() {
|
||||
if (this.$store.state.menus.length === 0) return null;
|
||||
|
||||
return this.$store.state.menus[this.$store.state.menus.length - 1];
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -65,7 +65,7 @@ export function selectFile(component: any, src: any, label: string | null, multi
|
|||
|
||||
};
|
||||
|
||||
component.$root.menu({
|
||||
component.$store.dispatch('showMenu', {
|
||||
items: [label ? {
|
||||
text: label,
|
||||
type: 'label'
|
||||
|
|
|
@ -115,6 +115,7 @@ export const store = createStore({
|
|||
text: string;
|
||||
result: any;
|
||||
}[],
|
||||
menus: [],
|
||||
postForm: null,
|
||||
fullView: false,
|
||||
|
||||
|
@ -277,6 +278,10 @@ export const store = createStore({
|
|||
state.dialogs = state.dialogs.filter(d => d.id !== dialogId);
|
||||
},
|
||||
|
||||
addMenu(state, menu) {
|
||||
state.menus.push(menu);
|
||||
},
|
||||
|
||||
setPostForm(state, postForm) {
|
||||
if (state.postForm != null && postForm != null) return;
|
||||
state.postForm = postForm;
|
||||
|
@ -392,6 +397,21 @@ export const store = createStore({
|
|||
});
|
||||
},
|
||||
|
||||
showMenu(ctx, opts) {
|
||||
return new Promise((res, rej) => {
|
||||
const menu = reactive({
|
||||
...opts,
|
||||
result: null,
|
||||
id: Math.random().toString() // TODO: uuidとか使う
|
||||
});
|
||||
ctx.commit('addMenu', menu);
|
||||
const unwatch = watch(() => menu.result, result => {
|
||||
unwatch();
|
||||
res(result);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
api(ctx, { endpoint, data, token }) {
|
||||
if (++ctx.state.pendingApiRequestsCount === 1) {
|
||||
// TODO: spinnerの表示はstoreでやらない
|
||||
|
|
|
@ -88,7 +88,7 @@ export default defineComponent({
|
|||
this.setSrc('list');
|
||||
}
|
||||
}));
|
||||
this.$root.menu({
|
||||
this.$store.dispatch('showMenu', {
|
||||
items: [{
|
||||
text: this.$t('_timelines.home'),
|
||||
icon: faHome,
|
||||
|
|
Loading…
Reference in New Issue