自前ルーティング (#6759)

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip
This commit is contained in:
syuilo
2020-10-25 01:21:41 +09:00
committed by GitHub
parent d4da5a1eea
commit 254cfaea28
59 changed files with 625 additions and 220 deletions

View File

@ -2,9 +2,9 @@
<span class="eiwwqkts" :class="{ cat }" :title="acct(user)" v-if="disableLink" v-user-preview="disablePreview ? undefined : user.id" @click="onClick">
<img class="inner" :src="url"/>
</span>
<router-link class="eiwwqkts" :class="{ cat }" :to="userPage(user)" :title="acct(user)" :target="target" v-else v-user-preview="disablePreview ? undefined : user.id">
<MkA class="eiwwqkts" :class="{ cat }" :to="userPage(user)" :title="acct(user)" :target="target" v-else v-user-preview="disablePreview ? undefined : user.id">
<img class="inner" :src="url"/>
</router-link>
</MkA>
</template>
<script lang="ts">

View File

@ -1,5 +1,5 @@
<template>
<router-link :to="`/channels/${channel.id}`" class="eftoefju _panel" tabindex="-1">
<MkA :to="`/channels/${channel.id}`" class="eftoefju _panel" tabindex="-1">
<div class="banner" v-if="channel.bannerUrl" :style="`background-image: url('${channel.bannerUrl}')`">
<div class="fade"></div>
<div class="name"><Fa :icon="faSatelliteDish"/> {{ channel.name }}</div>
@ -30,7 +30,7 @@
{{ $t('updatedAt') }}: <MkTime :time="channel.lastNotedAt"/>
</span>
</footer>
</router-link>
</MkA>
</template>
<script lang="ts">

View File

@ -1,6 +1,7 @@
import { App } from 'vue';
import mfm from './misskey-flavored-markdown.vue';
import a from './ui/a.vue';
import acct from './acct.vue';
import avatar from './avatar.vue';
import emoji from './emoji.vue';
@ -10,10 +11,10 @@ import time from './time.vue';
import url from './url.vue';
import loading from './loading.vue';
import error from './error.vue';
import streamIndicator from './stream-indicator.vue';
export default function(app: App) {
app.component('Mfm', mfm);
app.component('MkA', a);
app.component('MkAcct', acct);
app.component('MkAvatar', avatar);
app.component('MkEmoji', emoji);
@ -23,5 +24,4 @@ export default function(app: App) {
app.component('MkUrl', url);
app.component('MkLoading', loading);
app.component('MkError', error);
app.component('StreamIndicator', streamIndicator);
}

View File

@ -1,5 +1,5 @@
<template>
<component :is="self ? 'router-link' : 'a'" class="xlcxczvw _link" :[attr]="self ? url.substr(local.length) : url" :rel="rel" :target="target"
<component :is="self ? 'MkA' : 'a'" class="xlcxczvw _link" :[attr]="self ? url.substr(local.length) : url" :rel="rel" :target="target"
@mouseover="onMouseover"
@mouseleave="onMouseleave"
:title="url"

View File

@ -1,11 +1,11 @@
<template>
<router-link class="ldlomzub" :class="{ isMe }" :to="url" v-user-preview="canonical" v-if="url.startsWith('/')">
<MkA class="ldlomzub" :class="{ isMe }" :to="url" v-user-preview="canonical" v-if="url.startsWith('/')">
<span class="me" v-if="isMe">{{ $t('you') }}</span>
<span class="main">
<span class="username">@{{ username }}</span>
<span class="host" v-if="(host != localHost) || $store.state.settings.showFullAcct">@{{ toUnicode(host) }}</span>
</span>
</router-link>
</MkA>
<a class="ldlomzub" :href="url" target="_blank" rel="noopener" v-else>
<span class="main">
<span class="username">@{{ username }}</span>

View File

@ -9,8 +9,8 @@ import { concat } from '../../prelude/array';
import MkFormula from './formula.vue';
import MkCode from './code.vue';
import MkGoogle from './google.vue';
import MkA from './ui/a.vue';
import { host } from '@/config';
import { RouterLink } from 'vue-router';
export default defineComponent({
props: {
@ -150,7 +150,7 @@ export default defineComponent({
}
case 'hashtag': {
return [h(RouterLink, {
return [h(MkA, {
key: Math.random(),
to: this.isNote ? `/tags/${encodeURIComponent(token.node.props.hashtag)}` : `/explore/tags/${encodeURIComponent(token.node.props.hashtag)}`,
style: 'color:var(--hashtag);'

View File

@ -1,17 +1,17 @@
<template>
<header class="kkwtjztg">
<router-link class="name" :to="userPage(note.user)" v-user-preview="note.user.id">
<MkA class="name" :to="userPage(note.user)" v-user-preview="note.user.id">
<MkUserName :user="note.user"/>
</router-link>
</MkA>
<span class="is-bot" v-if="note.user.isBot">bot</span>
<span class="username"><MkAcct :user="note.user"/></span>
<span class="admin" v-if="note.user.isAdmin"><Fa :icon="faBookmark"/></span>
<span class="moderator" v-if="!note.user.isAdmin && note.user.isModerator"><Fa :icon="farBookmark"/></span>
<div class="info">
<span class="mobile" v-if="note.viaMobile"><Fa :icon="faMobileAlt"/></span>
<router-link class="created-at" :to="notePage(note)">
<MkA class="created-at" :to="notePage(note)">
<MkTime :time="note.createdAt"/>
</router-link>
</MkA>
<span class="visibility" v-if="note.visibility !== 'public'">
<Fa v-if="note.visibility === 'home'" :icon="faHome"/>
<Fa v-if="note.visibility === 'followers'" :icon="faUnlock"/>

View File

@ -18,9 +18,9 @@
<Fa :icon="faRetweet"/>
<i18n-t keypath="renotedBy" tag="span">
<template #user>
<router-link class="name" :to="userPage(note.user)" v-user-preview="note.userId">
<MkA class="name" :to="userPage(note.user)" v-user-preview="note.userId">
<MkUserName :user="note.user"/>
</router-link>
</MkA>
</template>
</i18n-t>
<div class="info">
@ -48,7 +48,7 @@
<div class="content" v-show="appearNote.cw == null || showContent">
<div class="text">
<span v-if="appearNote.isHidden" style="opacity: 0.5">({{ $t('private') }})</span>
<router-link class="reply" v-if="appearNote.replyId" :to="`/notes/${appearNote.replyId}`"><Fa :icon="faReply"/></router-link>
<MkA class="reply" v-if="appearNote.replyId" :to="`/notes/${appearNote.replyId}`"><Fa :icon="faReply"/></MkA>
<Mfm v-if="appearNote.text" :text="appearNote.text" :author="appearNote.user" :i="$store.state.i" :custom-emojis="appearNote.emojis"/>
<a class="rp" v-if="appearNote.renote != null">RN:</a>
</div>
@ -59,7 +59,7 @@
<MkUrlPreview v-for="url in urls" :url="url" :key="url" :compact="true" :detail="detail" class="url-preview"/>
<div class="renote" v-if="appearNote.renote"><XNotePreview :note="appearNote.renote"/></div>
</div>
<router-link v-if="appearNote.channel && !inChannel" class="channel" :to="`/channels/${appearNote.channel.id}`"><Fa :icon="faSatelliteDish"/> {{ appearNote.channel.name }}</router-link>
<MkA v-if="appearNote.channel && !inChannel" class="channel" :to="`/channels/${appearNote.channel.id}`"><Fa :icon="faSatelliteDish"/> {{ appearNote.channel.name }}</MkA>
</div>
<footer class="footer">
<XReactionsViewer :note="appearNote" ref="reactionsViewer"/>
@ -91,9 +91,9 @@
<div v-else class="_panel muted" @click="muted = false">
<i18n-t keypath="userSaysSomething" tag="small">
<template #name>
<router-link class="name" :to="userPage(appearNote.user)" v-user-preview="appearNote.userId">
<MkA class="name" :to="userPage(appearNote.user)" v-user-preview="appearNote.userId">
<MkUserName :user="appearNote.user"/>
</router-link>
</MkA>
</template>
</i18n-t>
</div>
@ -144,7 +144,7 @@ export default defineComponent({
inject: {
inChannel: {
default: null
}
},
},
props: {
@ -581,11 +581,6 @@ export default defineComponent({
});
menu = [{
type: 'link',
icon: faInfoCircle,
text: this.$t('details'),
to: '/notes/' + this.appearNote.id
}, null, {
icon: faCopy,
text: this.$t('copyContent'),
action: this.copyContent

View File

@ -18,34 +18,34 @@
</div>
<div class="tail">
<header>
<router-link v-if="notification.user" class="name" :to="userPage(notification.user)" v-user-preview="notification.user.id"><MkUserName :user="notification.user"/></router-link>
<MkA v-if="notification.user" class="name" :to="userPage(notification.user)" v-user-preview="notification.user.id"><MkUserName :user="notification.user"/></MkA>
<span v-else>{{ notification.header }}</span>
<MkTime :time="notification.createdAt" v-if="withTime"/>
</header>
<router-link v-if="notification.type === 'reaction'" class="text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)">
<MkA v-if="notification.type === 'reaction'" class="text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)">
<Fa :icon="faQuoteLeft"/>
<Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="!full" :custom-emojis="notification.note.emojis"/>
<Fa :icon="faQuoteRight"/>
</router-link>
<router-link v-if="notification.type === 'renote'" class="text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note.renote)">
</MkA>
<MkA v-if="notification.type === 'renote'" class="text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note.renote)">
<Fa :icon="faQuoteLeft"/>
<Mfm :text="getNoteSummary(notification.note.renote)" :plain="true" :nowrap="!full" :custom-emojis="notification.note.renote.emojis"/>
<Fa :icon="faQuoteRight"/>
</router-link>
<router-link v-if="notification.type === 'reply'" class="text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)">
</MkA>
<MkA v-if="notification.type === 'reply'" class="text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)">
<Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="!full" :custom-emojis="notification.note.emojis"/>
</router-link>
<router-link v-if="notification.type === 'mention'" class="text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)">
</MkA>
<MkA v-if="notification.type === 'mention'" class="text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)">
<Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="!full" :custom-emojis="notification.note.emojis"/>
</router-link>
<router-link v-if="notification.type === 'quote'" class="text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)">
</MkA>
<MkA v-if="notification.type === 'quote'" class="text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)">
<Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="!full" :custom-emojis="notification.note.emojis"/>
</router-link>
<router-link v-if="notification.type === 'pollVote'" class="text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)">
</MkA>
<MkA v-if="notification.type === 'pollVote'" class="text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)">
<Fa :icon="faQuoteLeft"/>
<Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="!full" :custom-emojis="notification.note.emojis"/>
<Fa :icon="faQuoteRight"/>
</router-link>
</MkA>
<span v-if="notification.type === 'follow'" class="text" style="opacity: 0.6;">{{ $t('youGotNewFollower') }}<div v-if="full"><MkFollowButton :user="notification.user" :full="true"/></div></span>
<span v-if="notification.type === 'followRequestAccepted'" class="text" style="opacity: 0.6;">{{ $t('followRequestAccepted') }}</span>
<span v-if="notification.type === 'receiveFollowRequest'" class="text" style="opacity: 0.6;">{{ $t('receiveFollowRequest') }}<div v-if="full && !followRequestDone"><button class="_textButton" @click="acceptFollowRequest()">{{ $t('accept') }}</button> | <button class="_textButton" @click="rejectFollowRequest()">{{ $t('reject') }}</button></div></span>

View File

@ -1,5 +1,5 @@
<template>
<router-link :to="`/@${page.user.username}/pages/${page.name}`" class="vhpxefrj" tabindex="-1">
<MkA :to="`/@${page.user.username}/pages/${page.name}`" class="vhpxefrj" tabindex="-1">
<div class="thumbnail" v-if="page.eyeCatchingImage" :style="`background-image: url('${page.eyeCatchingImage.thumbnailUrl}')`"></div>
<article>
<header>
@ -11,7 +11,7 @@
<p>{{ userName(page.user) }}</p>
</footer>
</article>
</router-link>
</MkA>
</template>
<script lang="ts">

View File

@ -1,11 +1,18 @@
<template>
<XWindow ref="window" :initial-width="400" :initial-height="500" :can-resize="true" @closed="$emit('closed')">
<XWindow ref="window"
:initial-width="400"
:initial-height="500"
:can-resize="true"
:close-right="true"
:contextmenu="contextmenu"
@closed="$emit('closed')"
>
<template #header>
<XHeader :info="pageInfo" :with-back="false"/>
</template>
<template #buttons>
<button class="_button" @click="expand" v-tooltip="$t('showInPage')"><Fa :icon="faExpandAlt"/></button>
<button class="_button" @click="popout" v-tooltip="$t('popout')"><Fa :icon="faExternalLinkAlt"/></button>
<button class="_button" @click="back()" v-if="history.length > 0"><Fa :icon="faChevronLeft"/></button>
<button class="_button" style="pointer-events: none;" v-else><!-- マージンのバランスを取るためのダミー --></button>
</template>
<div class="yrolvcoq" style="min-height: 100%; background: var(--bg);">
<component :is="component" v-bind="props" :ref="changePage"/>
@ -14,11 +21,13 @@
</template>
<script lang="ts">
import { defineComponent, markRaw } from 'vue';
import { faExternalLinkAlt, faExpandAlt } from '@fortawesome/free-solid-svg-icons';
import { defineComponent } from 'vue';
import { faExternalLinkAlt, faExpandAlt, faLink, faChevronLeft } from '@fortawesome/free-solid-svg-icons';
import XWindow from '@/components/ui/window.vue';
import XHeader from '@/ui/_common_/header.vue';
import { popout } from '@/scripts/popout';
import copyToClipboard from '@/scripts/copy-to-clipboard';
import { resolve } from '@/router';
export default defineComponent({
components: {
@ -26,6 +35,14 @@ export default defineComponent({
XHeader,
},
provide() {
return {
navHook: (url) => {
this.navigate(url);
}
};
},
props: {
initialUrl: {
type: String,
@ -38,7 +55,7 @@ export default defineComponent({
initialProps: {
type: Object,
required: false,
default: {},
default: () => {},
},
},
@ -50,18 +67,39 @@ export default defineComponent({
url: this.initialUrl,
component: this.initialComponent,
props: this.initialProps,
faExternalLinkAlt, faExpandAlt,
history: [],
faChevronLeft,
};
},
provide() {
return {
navHook: (url, component, props) => {
this.url = url;
this.component = markRaw(component);
this.props = props;
}
};
computed: {
contextmenu() {
return [{
type: 'label',
text: this.url,
}, {
icon: faExpandAlt,
text: this.$t('showInPage'),
action: this.expand
}, {
icon: faExternalLinkAlt,
text: this.$t('popout'),
action: this.popout
}, null, {
icon: faExternalLinkAlt,
text: this.$t('openInNewTab'),
action: () => {
window.open(this.url, '_blank');
this.$refs.window.close();
}
}, {
icon: faLink,
text: this.$t('copyLink'),
action: () => {
copyToClipboard(this.url);
}
}];
},
},
methods: {
@ -72,6 +110,18 @@ export default defineComponent({
}
},
navigate(url, record = true) {
if (record) this.history.push(this.url);
this.url = url;
const { component, props } = resolve(url);
this.component = component;
this.props = props;
},
back() {
this.navigate(this.history.pop(), false);
},
expand() {
this.$router.push(this.url);
this.$refs.window.close();

View File

@ -17,12 +17,12 @@
<button class="item _button index active" @click="top()" v-if="$route.name === 'index'">
<Fa :icon="faHome" fixed-width/><span class="text">{{ $store.getters.isSignedIn ? $t('timeline') : $t('home') }}</span>
</button>
<router-link class="item index" active-class="active" to="/" exact v-else>
<MkA class="item index" active-class="active" to="/" exact v-else>
<Fa :icon="faHome" fixed-width/><span class="text">{{ $store.getters.isSignedIn ? $t('timeline') : $t('home') }}</span>
</router-link>
</MkA>
<template v-for="item in menu">
<div v-if="item === '-'" class="divider"></div>
<component v-else-if="menuDef[item] && (menuDef[item].show !== false)" :is="menuDef[item].to ? 'router-link' : 'button'" class="item _button" :class="item" active-class="active" v-on="menuDef[item].action ? { click: menuDef[item].action } : {}" :to="menuDef[item].to">
<component v-else-if="menuDef[item] && (menuDef[item].show !== false)" :is="menuDef[item].to ? 'MkA' : 'button'" class="item _button" :class="item" active-class="active" v-on="menuDef[item].action ? { click: menuDef[item].action } : {}" :to="menuDef[item].to">
<Fa :icon="menuDef[item].icon" fixed-width/><span class="text">{{ $t(menuDef[item].title) }}</span>
<i v-if="menuDef[item].indicated"><Fa :icon="faCircle"/></i>
</component>
@ -35,9 +35,9 @@
<Fa :icon="faEllipsisH" fixed-width/><span class="text">{{ $t('more') }}</span>
<i v-if="otherNavItemIndicated"><Fa :icon="faCircle"/></i>
</button>
<router-link class="item" active-class="active" to="/settings">
<MkA class="item" active-class="active" to="/settings">
<Fa :icon="faCog" fixed-width/><span class="text">{{ $t('settings') }}</span>
</router-link>
</MkA>
</div>
</nav>
</transition>

View File

@ -1,70 +0,0 @@
<template>
<div class="nsbbhtug" v-if="hasDisconnected && $store.state.device.serverDisconnectedBehavior === 'quiet'" @click="resetDisconnected">
<div>{{ $t('disconnectedFromServer') }}</div>
<div class="command">
<button class="_textButton" @click="reload">{{ $t('reload') }}</button>
<button class="_textButton">{{ $t('doNothing') }}</button>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import * as os from '@/os';
export default defineComponent({
data() {
return {
hasDisconnected: false,
}
},
computed: {
stream() {
return os.stream;
},
},
created() {
os.stream.on('_disconnected_', this.onDisconnected);
},
beforeUnmount() {
os.stream.off('_disconnected_', this.onDisconnected);
},
methods: {
onDisconnected() {
this.hasDisconnected = true;
},
resetDisconnected() {
this.hasDisconnected = false;
},
reload() {
location.reload();
},
}
});
</script>
<style lang="scss" scoped>
.nsbbhtug {
position: fixed;
z-index: 16385;
bottom: 8px;
right: 8px;
margin: 0;
padding: 6px 12px;
font-size: 0.9em;
color: #fff;
background: #000;
opacity: 0.8;
border-radius: 4px;
max-width: 320px;
> .command {
display: flex;
justify-content: space-around;
> button {
padding: 0.7em;
}
}
}
</style>

View File

@ -3,9 +3,9 @@
<div class="body">
<span v-if="note.isHidden" style="opacity: 0.5">({{ $t('private') }})</span>
<span v-if="note.deletedAt" style="opacity: 0.5">({{ $t('deleted') }})</span>
<router-link class="reply" v-if="note.replyId" :to="`/notes/${note.replyId}`"><Fa :icon="faReply"/></router-link>
<MkA class="reply" v-if="note.replyId" :to="`/notes/${note.replyId}`"><Fa :icon="faReply"/></MkA>
<Mfm v-if="note.text" :text="note.text" :author="note.user" :i="$store.state.i" :custom-emojis="note.emojis"/>
<router-link class="rp" v-if="note.renoteId" :to="`/notes/${note.renoteId}`">RN: ...</router-link>
<MkA class="rp" v-if="note.renoteId" :to="`/notes/${note.renoteId}`">RN: ...</MkA>
</div>
<details v-if="note.files.length > 0">
<summary>({{ $t('withNFiles', { n: note.files.length }) }})</summary>

View File

@ -0,0 +1,104 @@
<template>
<a :href="to" :class="active ? activeClass : null" @click.prevent="nav" @contextmenu.prevent.stop="onContextmenu">
<slot></slot>
</a>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { faExpandAlt, faColumns, faExternalLinkAlt, faLink, faWindowMaximize } from '@fortawesome/free-solid-svg-icons';
import * as os from '@/os';
import copyToClipboard from '@/scripts/copy-to-clipboard';
import { router } from '@/router';
import { deckmode } from '@/config';
export default defineComponent({
inject: {
navHook: {
default: null
},
sideViewHook: {
default: null
}
},
props: {
to: {
type: String,
required: true,
},
activeClass: {
type: String,
required: false,
},
},
computed: {
active() {
if (this.activeClass == null) return false;
const resolved = router.resolve(this.to);
if (resolved.path == this.$route.path) return true;
if (resolved.name == null) return false;
if (this.$route.name == null) return false;
return resolved.name == this.$route.name;
}
},
methods: {
onContextmenu(e) {
if (window.getSelection().toString() !== '') return;
os.contextMenu([{
type: 'label',
text: this.to,
}, {
icon: faWindowMaximize,
text: this.$t('openInWindow'),
action: () => {
os.pageWindow(this.to);
}
}, !this.navHook && this.sideViewHook ? {
icon: faColumns,
text: this.$t('openInSideView'),
action: () => {
this.sideViewHook(this.to);
}
} : undefined, {
icon: faExpandAlt,
text: this.$t('showInPage'),
action: () => {
this.$router.push(this.to);
}
}, null, {
icon: faExternalLinkAlt,
text: this.$t('openInNewTab'),
action: () => {
window.open(this.to, '_blank');
}
}, {
icon: faLink,
text: this.$t('copyLink'),
action: () => {
copyToClipboard(this.to);
}
}], e);
},
nav() {
if (this.navHook) {
this.navHook(this.to);
} else {
if (this.$store.state.device.defaultSideView && this.sideViewHook && this.to !== '/') {
this.sideViewHook(this.to);
return;
}
if (this.$store.state.device.deckNavWindow && deckmode && this.to !== '/') {
os.pageWindow(this.to);
return;
}
this.$router.push(this.to);
}
}
}
});
</script>

View File

@ -1,5 +1,5 @@
<template>
<div class="nvlagfpb">
<div class="nvlagfpb" @contextmenu.prevent.stop="() => {}">
<MkMenu :items="items" @close="$emit('closed')" class="_popup _shadow" :align="'left'"/>
</div>
</template>

View File

@ -12,12 +12,12 @@
<span v-else-if="item.type === 'pending'" :tabindex="i" class="pending item">
<span><MkEllipsis/></span>
</span>
<router-link v-else-if="item.type === 'link'" :to="item.to" @click.passive="close()" :tabindex="i" class="_button item">
<MkA v-else-if="item.type === 'link'" :to="item.to" @click.passive="close()" :tabindex="i" class="_button item">
<Fa v-if="item.icon" :icon="item.icon" fixed-width/>
<MkAvatar v-if="item.avatar" :user="item.avatar" class="avatar"/>
<span>{{ item.text }}</span>
<i v-if="item.indicate"><Fa :icon="faCircle"/></i>
</router-link>
</MkA>
<a v-else-if="item.type === 'a'" :href="item.href" :target="item.target" :download="item.download" @click="close()" :tabindex="i" class="_button item">
<Fa v-if="item.icon" :icon="item.icon" fixed-width/>
<span>{{ item.text }}</span>

View File

@ -51,7 +51,7 @@ export default defineComponent({
.novjtctn {
position: relative;
display: inline-block;
margin: 0 32px 0 0;
margin: 16px 32px 0 0;
cursor: pointer;
transition: all 0.3s;

View File

@ -2,14 +2,16 @@
<transition :name="$store.state.device.animation ? 'window' : ''" appear @after-leave="$emit('closed')">
<div class="ebkgocck" v-if="showing">
<div class="body _popup _shadow _narrow_" @mousedown="onBodyMousedown" @keydown="onKeydown">
<div class="header">
<button class="_button" @click="close()"><Fa :icon="faTimes"/></button>
<div class="header" @contextmenu.prevent.stop="onContextmenu">
<slot v-if="closeRight" name="buttons"><button class="_button" style="pointer-events: none;"></button></slot>
<button v-else class="_button" @click="close()"><Fa :icon="faTimes"/></button>
<span class="title" @mousedown.prevent="onHeaderMousedown" @touchstart.prevent="onHeaderMousedown">
<slot name="header"></slot>
</span>
<slot name="buttons">
<button class="_button" style="pointer-events: none;"></button>
</slot>
<button v-if="closeRight" class="_button" @click="close()"><Fa :icon="faTimes"/></button>
<slot v-else name="buttons"><button class="_button" style="pointer-events: none;"></button></slot>
</div>
<div class="body" v-if="padding">
<div class="_section">
@ -85,6 +87,15 @@ export default defineComponent({
required: false,
default: false,
},
closeRight: {
type: Boolean,
required: false,
default: false,
},
contextmenu: {
type: Array,
required: false,
}
},
emits: ['closed'],
@ -129,6 +140,12 @@ export default defineComponent({
}
},
onContextmenu(e) {
if (this.contextmenu) {
os.contextMenu(this.contextmenu, e);
}
},
// 最前面へ移動
top() {
let z = 0;

View File

@ -1,136 +0,0 @@
<template>
<div class="mk-uploader _acrylic">
<ol v-if="uploads.length > 0">
<li v-for="ctx in uploads" :key="ctx.id">
<div class="img" :style="{ backgroundImage: `url(${ ctx.img })` }"></div>
<div class="top">
<p class="name"><Fa :icon="faSpinner" pulse/>{{ ctx.name }}</p>
<p class="status">
<span class="initing" v-if="ctx.progressValue === undefined">{{ $t('waiting') }}<MkEllipsis/></span>
<span class="kb" v-if="ctx.progressValue !== undefined">{{ String(Math.floor(ctx.progressValue / 1024)).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,') }}<i>KB</i> / {{ String(Math.floor(ctx.progressMax / 1024)).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,') }}<i>KB</i></span>
<span class="percentage" v-if="ctx.progressValue !== undefined">{{ Math.floor((ctx.progressValue / ctx.progressMax) * 100) }}</span>
</p>
</div>
<progress :value="ctx.progressValue || 0" :max="ctx.progressMax || 0" :class="{ initing: ctx.progressValue === undefined, waiting: ctx.progressValue !== undefined && ctx.progressValue === ctx.progressMax }"></progress>
</li>
</ol>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { faSpinner } from '@fortawesome/free-solid-svg-icons';
import * as os from '@/os';
export default defineComponent({
data() {
return {
uploads: os.uploads,
faSpinner
};
},
});
</script>
<style lang="scss" scoped>
.mk-uploader {
position: fixed;
z-index: 10000;
right: 16px;
width: 260px;
top: 32px;
padding: 16px 20px;
pointer-events: none;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
border-radius: 8px;
}
.mk-uploader:empty {
display: none;
}
.mk-uploader > ol {
display: block;
margin: 0;
padding: 0;
list-style: none;
}
.mk-uploader > ol > li {
display: grid;
margin: 8px 0 0 0;
padding: 0;
height: 36px;
width: 100%;
border-top: solid 8px transparent;
grid-template-columns: 36px calc(100% - 44px);
grid-template-rows: 1fr 8px;
column-gap: 8px;
box-sizing: content-box;
}
.mk-uploader > ol > li:first-child {
margin: 0;
box-shadow: none;
border-top: none;
}
.mk-uploader > ol > li > .img {
display: block;
background-size: cover;
background-position: center center;
grid-column: 1/2;
grid-row: 1/3;
}
.mk-uploader > ol > li > .top {
display: flex;
grid-column: 2/3;
grid-row: 1/2;
}
.mk-uploader > ol > li > .top > .name {
display: block;
padding: 0 8px 0 0;
margin: 0;
font-size: 0.8em;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
flex-shrink: 1;
}
.mk-uploader > ol > li > .top > .name > [data-icon] {
margin-right: 4px;
}
.mk-uploader > ol > li > .top > .status {
display: block;
margin: 0 0 0 auto;
padding: 0;
font-size: 0.8em;
flex-shrink: 0;
}
.mk-uploader > ol > li > .top > .status > .initing {
}
.mk-uploader > ol > li > .top > .status > .kb {
}
.mk-uploader > ol > li > .top > .status > .percentage {
display: inline-block;
width: 48px;
text-align: right;
}
.mk-uploader > ol > li > .top > .status > .percentage:after {
content: '%';
}
.mk-uploader > ol > li > progress {
display: block;
background: transparent;
border: none;
border-radius: 4px;
overflow: hidden;
grid-column: 2/3;
grid-row: 2/3;
z-index: 2;
width: 100%;
height: 8px;
}
.mk-uploader > ol > li > progress::-webkit-progress-value {
background: var(--accent);
}
.mk-uploader > ol > li > progress::-webkit-progress-bar {
//background: var(--accentAlpha01);
background: transparent;
}
</style>

View File

@ -8,7 +8,7 @@
</div>
<div v-else class="mk-url-preview" v-size="{ max: [400, 350] }">
<transition name="zoom" mode="out-in">
<component :is="self ? 'router-link' : 'a'" :class="{ compact }" :[attr]="self ? url.substr(local.length) : url" rel="nofollow noopener" :target="target" :title="url" v-if="!fetching">
<component :is="self ? 'MkA' : 'a'" :class="{ compact }" :[attr]="self ? url.substr(local.length) : url" rel="nofollow noopener" :target="target" :title="url" v-if="!fetching">
<div class="thumbnail" v-if="thumbnail" :style="`background-image: url('${thumbnail}')`">
<button class="_button" v-if="!playerEnabled && player.url" @click.prevent="playerEnabled = true" :title="$t('enablePlayer')"><Fa :icon="faPlayCircle"/></button>
</div>

View File

@ -1,5 +1,5 @@
<template>
<component :is="self ? 'router-link' : 'a'" class="ieqqeuvs _link" :[attr]="self ? url.substr(local.length) : url" :rel="rel" :target="target"
<component :is="self ? 'MkA' : 'a'" class="ieqqeuvs _link" :[attr]="self ? url.substr(local.length) : url" :rel="rel" :target="target"
@mouseover="onMouseover"
@mouseleave="onMouseleave"
>

View File

@ -3,7 +3,7 @@
<div class="banner" :style="user.bannerUrl ? `background-image: url(${user.bannerUrl})` : ''"></div>
<MkAvatar class="avatar" :user="user" :disable-preview="true"/>
<div class="title">
<router-link class="name" :to="userPage(user)"><MkUserName :user="user" :nowrap="false"/></router-link>
<MkA class="name" :to="userPage(user)"><MkUserName :user="user" :nowrap="false"/></MkA>
<p class="username"><MkAcct :user="user"/></p>
</div>
<div class="description">

View File

@ -5,7 +5,7 @@
<div class="banner" :style="user.bannerUrl ? `background-image: url(${user.bannerUrl})` : ''"></div>
<MkAvatar class="avatar" :user="user" :disable-preview="true"/>
<div class="title">
<router-link class="name" :to="userPage(user)"><MkUserName :user="user" :nowrap="false"/></router-link>
<MkA class="name" :to="userPage(user)"><MkUserName :user="user" :nowrap="false"/></MkA>
<p class="username"><MkAcct :user="user"/></p>
</div>
<div class="description">

View File

@ -6,13 +6,13 @@
</div>
<div class="users">
<router-link v-for="item in items" class="user" :key="item.id" :to="userPage(extract ? extract(item) : item)">
<MkA v-for="item in items" class="user" :key="item.id" :to="userPage(extract ? extract(item) : item)">
<MkAvatar :user="extract ? extract(item) : item" class="avatar" :disable-link="true"/>
<div class="body">
<MkUserName :user="extract ? extract(item) : item" class="name"/>
<MkAcct :user="extract ? extract(item) : item" class="acct"/>
</div>
</router-link>
</MkA>
</div>
<button class="more _button" v-appear="$store.state.device.enableInfiniteScroll ? fetchMore : null" @click="fetchMore" v-show="more" :disabled="moreFetching">
<template v-if="!moreFetching">{{ $t('loadMore') }}</template>