diff options
Diffstat (limited to 'src/client/app/common')
15 files changed, 425 insertions, 118 deletions
diff --git a/src/client/app/common/scripts/can-hide-text.ts b/src/client/app/common/scripts/can-hide-text.ts deleted file mode 100644 index 4a4be8d9d0..0000000000 --- a/src/client/app/common/scripts/can-hide-text.ts +++ /dev/null @@ -1,16 +0,0 @@ -export default function(note) { - if (note.text == null) return true; - - let txt = note.text; - - if (note.media) { - note.media.forEach(file => { - txt = txt.replace(file.url, ''); - if (file.src) txt = txt.replace(file.src, ''); - }); - - if (txt == '') return true; - } - - return false; -} diff --git a/src/client/app/common/scripts/check-for-update.ts b/src/client/app/common/scripts/check-for-update.ts index b5ba6916d1..4445eefc39 100644 --- a/src/client/app/common/scripts/check-for-update.ts +++ b/src/client/app/common/scripts/check-for-update.ts @@ -23,7 +23,10 @@ export default async function(mios: MiOS, force = false, silent = false) { } if (!silent) { - alert('%i18n:common.update-available%'.replace('{newer}', newer).replace('{current}', current)); + mios.apis.dialog({ + title: '%i18n:common.update-available-title%', + text: '%i18n:common.update-available%'.replace('{newer}', newer).replace('{current}', current) + }); } return newer; diff --git a/src/client/app/common/scripts/compose-notification.ts b/src/client/app/common/scripts/compose-notification.ts index cc28f75998..2e58649ac2 100644 --- a/src/client/app/common/scripts/compose-notification.ts +++ b/src/client/app/common/scripts/compose-notification.ts @@ -20,34 +20,6 @@ export default function(type, data): Notification { icon: data.url + '?thumbnail&size=64' }; - case 'mention': - return { - title: `${getUserName(data.user)}さんから:`, - body: getNoteSummary(data), - icon: data.user.avatarUrl + '?thumbnail&size=64' - }; - - case 'reply': - return { - title: `${getUserName(data.user)}さんから返信:`, - body: getNoteSummary(data), - icon: data.user.avatarUrl + '?thumbnail&size=64' - }; - - case 'quote': - return { - title: `${getUserName(data.user)}さんが引用:`, - body: getNoteSummary(data), - icon: data.user.avatarUrl + '?thumbnail&size=64' - }; - - case 'reaction': - return { - title: `${getUserName(data.user)}: ${getReactionEmoji(data.reaction)}:`, - body: getNoteSummary(data.note), - icon: data.user.avatarUrl + '?thumbnail&size=64' - }; - case 'unread_messaging_message': return { title: `${getUserName(data.user)}さんからメッセージ:`, @@ -62,6 +34,40 @@ export default function(type, data): Notification { icon: data.parent.avatarUrl + '?thumbnail&size=64' }; + case 'notification': + switch (data.type) { + case 'mention': + return { + title: `${getUserName(data.user)}さんから:`, + body: getNoteSummary(data), + icon: data.user.avatarUrl + '?thumbnail&size=64' + }; + + case 'reply': + return { + title: `${getUserName(data.user)}さんから返信:`, + body: getNoteSummary(data), + icon: data.user.avatarUrl + '?thumbnail&size=64' + }; + + case 'quote': + return { + title: `${getUserName(data.user)}さんが引用:`, + body: getNoteSummary(data), + icon: data.user.avatarUrl + '?thumbnail&size=64' + }; + + case 'reaction': + return { + title: `${getUserName(data.user)}: ${getReactionEmoji(data.reaction)}:`, + body: getNoteSummary(data.note), + icon: data.user.avatarUrl + '?thumbnail&size=64' + }; + + default: + return null; + } + default: return null; } diff --git a/src/client/app/common/scripts/get-kao.ts b/src/client/app/common/scripts/get-kao.ts index 2168c5be88..d380187510 100644 --- a/src/client/app/common/scripts/get-kao.ts +++ b/src/client/app/common/scripts/get-kao.ts @@ -1,5 +1,5 @@ export default () => [ '(=^・・^=)', - 'v(‘ω’)v', + 'v('ω')v', '🐡( \'-\' 🐡 )フグパンチ!!!!' ][Math.floor(Math.random() * 3)]; diff --git a/src/client/app/common/views/components/google.vue b/src/client/app/common/views/components/google.vue index 92817d3c1f..8272961ef2 100644 --- a/src/client/app/common/views/components/google.vue +++ b/src/client/app/common/views/components/google.vue @@ -1,7 +1,7 @@ <template> <div class="mk-google"> <input type="search" v-model="query" :placeholder="q"> - <button @click="search">検索</button> + <button @click="search">%fa:search% %i18n:common.search%</button> </div> </template> diff --git a/src/client/app/common/views/components/index.ts b/src/client/app/common/views/components/index.ts index 5b2fa084fb..e2cba2e53b 100644 --- a/src/client/app/common/views/components/index.ts +++ b/src/client/app/common/views/components/index.ts @@ -9,7 +9,7 @@ import forkit from './forkit.vue'; import acct from './acct.vue'; import avatar from './avatar.vue'; import nav from './nav.vue'; -import noteHtml from './note-html'; +import misskeyFlavoredMarkdown from './misskey-flavored-markdown'; import poll from './poll.vue'; import pollEditor from './poll-editor.vue'; import reactionIcon from './reaction-icon.vue'; @@ -47,7 +47,7 @@ Vue.component('mk-forkit', forkit); Vue.component('mk-acct', acct); Vue.component('mk-avatar', avatar); Vue.component('mk-nav', nav); -Vue.component('mk-note-html', noteHtml); +Vue.component('misskey-flavored-markdown', misskeyFlavoredMarkdown); Vue.component('mk-poll', poll); Vue.component('mk-poll-editor', pollEditor); Vue.component('mk-reaction-icon', reactionIcon); diff --git a/src/client/app/common/views/components/messaging-room.message.vue b/src/client/app/common/views/components/messaging-room.message.vue index a77b5f3658..f33173da6f 100644 --- a/src/client/app/common/views/components/messaging-room.message.vue +++ b/src/client/app/common/views/components/messaging-room.message.vue @@ -8,7 +8,7 @@ <img src="/assets/desktop/messaging/delete.png" alt="Delete"/> </button> <div class="content" v-if="!message.isDeleted"> - <mk-note-html class="text" v-if="message.text" ref="text" :text="message.text" :i="$store.state.i"/> + <misskey-flavored-markdown class="text" v-if="message.text" ref="text" :text="message.text" :i="$store.state.i"/> <div class="file" v-if="message.file"> <a :href="message.file.url" target="_blank" :title="message.file.name"> <img v-if="message.file.type.split('/')[0] == 'image'" :src="message.file.url" :alt="message.file.name"/> @@ -32,7 +32,7 @@ <script lang="ts"> import Vue from 'vue'; -import parse from '../../../../../text/parse'; +import parse from '../../../../../mfm/parse'; export default Vue.extend({ props: { diff --git a/src/client/app/common/views/components/note-html.ts b/src/client/app/common/views/components/misskey-flavored-markdown.ts index 8fa5f380dd..c321c76104 100644 --- a/src/client/app/common/views/components/note-html.ts +++ b/src/client/app/common/views/components/misskey-flavored-markdown.ts @@ -1,6 +1,6 @@ import Vue from 'vue'; import * as emojilib from 'emojilib'; -import parse from '../../../../../text/parse'; +import parse from '../../../../../mfm/parse'; import getAcct from '../../../../../acct/render'; import { url } from '../../../config'; import MkUrl from './url.vue'; @@ -10,7 +10,7 @@ const flatten = list => list.reduce( (a, b) => a.concat(Array.isArray(b) ? flatten(b) : b), [] ); -export default Vue.component('mk-note-html', { +export default Vue.component('misskey-flavored-markdown', { props: { text: { type: String, @@ -40,17 +40,6 @@ export default Vue.component('mk-note-html', { ast = this.ast; } - if (ast.filter(x => x.type != 'hashtag').length == 0) { - return; - } - - while (ast[ast.length - 1] && ( - ast[ast.length - 1].type == 'hashtag' || - (ast[ast.length - 1].type == 'text' && ast[ast.length - 1].content == ' ') || - (ast[ast.length - 1].type == 'text' && ast[ast.length - 1].content == '\n'))) { - ast.pop(); - } - // Parse ast to DOM const els = flatten(ast.map(token => { switch (token.type) { diff --git a/src/client/app/common/views/components/note-header.vue b/src/client/app/common/views/components/note-header.vue index 6e64a6a6d3..25a3339264 100644 --- a/src/client/app/common/views/components/note-header.vue +++ b/src/client/app/common/views/components/note-header.vue @@ -72,6 +72,7 @@ root(isDark) > .is-admin > .is-bot > .is-cat + flex-shrink 0 align-self center margin 0 .5em 0 0 padding 1px 6px @@ -89,6 +90,7 @@ root(isDark) overflow hidden text-overflow ellipsis color isDark ? #606984 : #ccc + flex-shrink 2147483647 > .info margin-left auto diff --git a/src/client/app/common/views/components/reaction-picker.vue b/src/client/app/common/views/components/reaction-picker.vue index 0db6f66b37..ed7aedb58e 100644 --- a/src/client/app/common/views/components/reaction-picker.vue +++ b/src/client/app/common/views/components/reaction-picker.vue @@ -1,7 +1,7 @@ <template> <div class="mk-reaction-picker"> <div class="backdrop" ref="backdrop" @click="close"></div> - <div class="popover" :class="{ compact }" ref="popover"> + <div class="popover" :class="{ compact, big }" ref="popover"> <p v-if="!compact">{{ title }}</p> <div> <button @click="react('like')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="1" title="%i18n:common.reactions.like%"><mk-reaction-icon reaction='like'/></button> @@ -25,7 +25,28 @@ import * as anime from 'animejs'; const placeholder = '%i18n:@choose-reaction%'; export default Vue.extend({ - props: ['note', 'source', 'compact', 'cb'], + props: { + note: { + type: Object, + required: true + }, + source: { + required: true + }, + compact: { + type: Boolean, + required: false, + default: false + }, + cb: { + required: false + }, + big: { + type: Boolean, + required: false, + default: false + } + }, data() { return { title: placeholder @@ -162,6 +183,16 @@ root(isDark) border-right solid $balloon-size transparent border-bottom solid $balloon-size $bgcolor + &.compact + > div + width 280px + + > button + width 50px + height 50px + font-size 28px + border-radius 4px + > p display block margin 0 diff --git a/src/client/app/common/views/components/reversi.game.vue b/src/client/app/common/views/components/reversi.game.vue index dc79c95bb8..a2a6fd0dc1 100644 --- a/src/client/app/common/views/components/reversi.game.vue +++ b/src/client/app/common/views/components/reversi.game.vue @@ -3,24 +3,39 @@ <header><b>{{ blackUser.name }}</b>(黒) vs <b>{{ whiteUser.name }}</b>(白)</header> <div style="overflow: hidden"> - <p class="turn" v-if="!iAmPlayer && !game.isEnded">{{ turnUser.name }}のターンです<mk-ellipsis/></p> - <p class="turn" v-if="logPos != logs.length">{{ turnUser.name }}のターン</p> - <p class="turn1" v-if="iAmPlayer && !game.isEnded && !isMyTurn">相手のターンです<mk-ellipsis/></p> - <p class="turn2" v-if="iAmPlayer && !game.isEnded && isMyTurn" v-animate-css="{ classes: 'tada', iteration: 'infinite' }">あなたのターンです</p> + <p class="turn" v-if="!iAmPlayer && !game.isEnded">{{ '%i18n:common.reversi.turn-of%'.replace('{}', turnUser.name) }}<mk-ellipsis/></p> + <p class="turn" v-if="logPos != logs.length">{{ '%i18n:common.reversi.past-turn-of%'.replace('{}', turnUser.name) }}</p> + <p class="turn1" v-if="iAmPlayer && !game.isEnded && !isMyTurn">%i18n:common.reversi.opponent-turn%<mk-ellipsis/></p> + <p class="turn2" v-if="iAmPlayer && !game.isEnded && isMyTurn" v-animate-css="{ classes: 'tada', iteration: 'infinite' }">%i18n:common.reversi.my-turn%</p> <p class="result" v-if="game.isEnded && logPos == logs.length"> <template v-if="game.winner"><b>{{ game.winner.name }}</b>の勝ち{{ game.settings.isLlotheo ? ' (ロセオ)' : '' }}</template> - <template v-else>引き分け</template> + <template v-else>%i18n:common.reversi.drawn%</template> </p> </div> - <div class="board" :style="{ 'grid-template-rows': `repeat(${ game.settings.map.length }, 1fr)`, 'grid-template-columns': `repeat(${ game.settings.map[0].length }, 1fr)` }"> - <div v-for="(stone, i) in o.board" - :class="{ empty: stone == null, none: o.map[i] == 'null', isEnded: game.isEnded, myTurn: !game.isEnded && isMyTurn, can: turnUser ? o.canPut(turnUser.id == blackUser.id, i) : null, prev: o.prevPos == i }" - @click="set(i)" - :title="'[' + (o.transformPosToXy(i)[0] + 1) + ', ' + (o.transformPosToXy(i)[1] + 1) + '] (' + i + ')'" - > - <img v-if="stone === true" :src="`${blackUser.avatarUrl}?thumbnail&size=128`" alt=""> - <img v-if="stone === false" :src="`${whiteUser.avatarUrl}?thumbnail&size=128`" alt=""> + <div class="board"> + <div class="labels-x" v-if="this.$store.state.settings.reversiBoardLabels"> + <span v-for="i in game.settings.map[0].length">{{ String.fromCharCode(64 + i) }}</span> + </div> + <div class="flex"> + <div class="labels-y" v-if="this.$store.state.settings.reversiBoardLabels"> + <div v-for="i in game.settings.map.length">{{ i }}</div> + </div> + <div class="cells" :style="cellsStyle"> + <div v-for="(stone, i) in o.board" + :class="{ empty: stone == null, none: o.map[i] == 'null', isEnded: game.isEnded, myTurn: !game.isEnded && isMyTurn, can: turnUser ? o.canPut(turnUser.id == blackUser.id, i) : null, prev: o.prevPos == i }" + @click="set(i)" + :title="`${String.fromCharCode(65 + o.transformPosToXy(i)[0])}${o.transformPosToXy(i)[1] + 1}`"> + <img v-if="stone === true" :src="`${blackUser.avatarUrl}?thumbnail&size=128`" alt=""> + <img v-if="stone === false" :src="`${whiteUser.avatarUrl}?thumbnail&size=128`" alt=""> + </div> + </div> + <div class="labels-y" v-if="this.$store.state.settings.reversiBoardLabels"> + <div v-for="i in game.settings.map.length">{{ i }}</div> + </div> + </div> + <div class="labels-x" v-if="this.$store.state.settings.reversiBoardLabels"> + <span v-for="i in game.settings.map[0].length">{{ String.fromCharCode(64 + i) }}</span> </div> </div> @@ -92,6 +107,12 @@ export default Vue.extend({ isMyTurn(): boolean { if (this.turnUser == null) return null; return this.turnUser.id == this.$store.state.i.id; + }, + cellsStyle(): any { + return { + 'grid-template-rows': `repeat(${ this.game.settings.map.length }, 1fr)`, + 'grid-template-columns': `repeat(${ this.game.settings.map[0].length }, 1fr)` + }; } }, @@ -244,54 +265,99 @@ export default Vue.extend({ border-bottom dashed 1px #c4cdd4 > .board - display grid - grid-gap 4px - width 350px - height 350px + width calc(100% - 16px) + max-width 500px margin 0 auto - > div - background transparent - border-radius 6px - overflow hidden + $label-size = 16px + $gap = 4px + + > .labels-x + height $label-size + padding 0 $label-size + display flex + + > * + flex 1 + display flex + align-items center + justify-content center + font-size 12px + + &:first-child + margin-left -($gap / 2) + + &:last-child + margin-right -($gap / 2) + + > .flex + display flex + + > .labels-y + width $label-size + display flex + flex-direction column + + > * + flex 1 + display flex + align-items center + justify-content center + font-size 12px + + &:first-child + margin-top -($gap / 2) + + &:last-child + margin-bottom -($gap / 2) + + > .cells + flex 1 + display grid + grid-gap $gap + + > div + background transparent + border-radius 6px + overflow hidden - * - pointer-events none - user-select none + * + pointer-events none + user-select none - &.empty - border solid 2px #eee + &.empty + border solid 2px #eee - &.empty.can - background #eee + &.empty.can + background #eee - &.empty.myTurn - border-color #ddd + &.empty.myTurn + border-color #ddd - &.can - background #eee - cursor pointer + &.can + background #eee + cursor pointer - &:hover - border-color darken($theme-color, 10%) - background $theme-color + &:hover + border-color darken($theme-color, 10%) + background $theme-color - &:active - background darken($theme-color, 10%) + &:active + background darken($theme-color, 10%) - &.prev - box-shadow 0 0 0 4px rgba($theme-color, 0.7) + &.prev + box-shadow 0 0 0 4px rgba($theme-color, 0.7) - &.isEnded - border-color #ddd + &.isEnded + border-color #ddd - &.none - border-color transparent !important + &.none + border-color transparent !important - > img - display block - width 100% - height 100% + > img + display block + width 100% + height 100% > .graph display grid diff --git a/src/client/app/common/views/components/reversi.vue b/src/client/app/common/views/components/reversi.vue index e4d7740bde..61705163ac 100644 --- a/src/client/app/common/views/components/reversi.vue +++ b/src/client/app/common/views/components/reversi.vue @@ -10,7 +10,7 @@ </div> </div> <div class="index" v-else> - <h1>Misskey %fa:circle%thell%fa:circle R%</h1> + <h1>Misskey Reversi</h1> <p>他のMisskeyユーザーとリバーシで対戦しよう</p> <div class="play"> <el-button round>フリーマッチ(準備中)</el-button> diff --git a/src/client/app/common/views/components/welcome-timeline.vue b/src/client/app/common/views/components/welcome-timeline.vue index f3372bf062..5a8b9df476 100644 --- a/src/client/app/common/views/components/welcome-timeline.vue +++ b/src/client/app/common/views/components/welcome-timeline.vue @@ -13,7 +13,7 @@ </div> </header> <div class="text"> - <mk-note-html v-if="note.text" :text="note.text"/> + <misskey-flavored-markdown v-if="note.text" :text="note.text"/> </div> </div> </div> @@ -24,6 +24,13 @@ import Vue from 'vue'; export default Vue.extend({ + props: { + max: { + type: Number, + required: false, + default: undefined + } + }, data() { return { fetching: true, @@ -37,6 +44,7 @@ export default Vue.extend({ fetch(cb?) { this.fetching = true; (this as any).api('notes', { + limit: this.max, local: true, reply: false, renote: false, diff --git a/src/client/app/common/views/pages/follow.vue b/src/client/app/common/views/pages/follow.vue new file mode 100644 index 0000000000..c8e838be85 --- /dev/null +++ b/src/client/app/common/views/pages/follow.vue @@ -0,0 +1,215 @@ +<template> +<div class="syxhndwprovvuqhmyvveewmbqayniwkv" v-if="!fetching" :data-darkmode="$store.state.device.darkmode"> + <div class="signed-in-as" v-html="'%i18n:@signed-in-as%'.replace('{}', '<b>' + myName + '</b>')"></div> + + <main> + <div class="banner" :style="bannerStyle"></div> + <mk-avatar class="avatar" :user="user" :disable-preview="true"/> + <div class="body"> + <router-link :to="user | userPage" class="name">{{ user | userName }}</router-link> + <span class="username">@{{ user | acct }}</span> + <div class="description"> + <misskey-flavored-markdown v-if="user.description" :text="user.description" :i="$store.state.i"/> + </div> + </div> + </main> + + <button + :class="{ wait: followWait, active: user.isFollowing || user.hasPendingFollowRequestFromYou }" + @click="onClick" + :disabled="followWait"> + <template v-if="!followWait"> + <template v-if="user.hasPendingFollowRequestFromYou">%fa:hourglass-half% %i18n:@request-pending%</template> + <template v-else-if="user.isFollowing">%fa:minus% %i18n:@following%</template> + <template v-else-if="!user.isFollowing && user.isLocked">%fa:plus% %i18n:@follow-request%</template> + <template v-else-if="!user.isFollowing && !user.isLocked">%fa:plus% %i18n:@follow%</template> + </template> + <template v-else>%fa:spinner .pulse .fw%</template> + </button> +</div> +</template> + +<script lang="ts"> +import Vue from 'vue'; +import parseAcct from '../../../../../acct/parse'; +import getUserName from '../../../../../renderers/get-user-name'; +import Progress from '../../../common/scripts/loading'; + +export default Vue.extend({ + data() { + return { + fetching: true, + user: null, + followWait: false + }; + }, + + computed: { + myName(): string { + return Vue.filter('userName')(this.$store.state.i); + }, + + bannerStyle(): any { + if (this.user.bannerUrl == null) return {}; + return { + backgroundColor: this.user.bannerColor && this.user.bannerColor.length == 3 ? `rgb(${ this.user.bannerColor.join(',') })` : null, + backgroundImage: `url(${ this.user.bannerUrl })` + }; + } + }, + + created() { + this.fetch(); + }, + + methods: { + fetch() { + const acct = new URL(location.href).searchParams.get('acct'); + this.fetching = true; + Progress.start(); + (this as any).api('users/show', parseAcct(acct)).then(user => { + this.user = user; + this.fetching = false; + Progress.done(); + document.title = getUserName(this.user) + ' | Misskey'; + }); + }, + + async onClick() { + this.followWait = true; + + try { + if (this.user.isFollowing) { + this.user = await (this as any).api('following/delete', { + userId: this.user.id + }); + } else { + if (this.user.isLocked && this.user.hasPendingFollowRequestFromYou) { + this.user = await (this as any).api('following/requests/cancel', { + userId: this.user.id + }); + } else if (this.user.isLocked) { + this.user = await (this as any).api('following/create', { + userId: this.user.id + }); + } else { + this.user = await (this as any).api('following/create', { + userId: this.user.id + }); + } + } + } catch (e) { + console.error(e); + } finally { + this.followWait = false; + } + } + } +}); +</script> + +<style lang="stylus" scoped> +@import '~const.styl' + +root(isDark) + padding 32px + max-width 500px + margin 0 auto + text-align center + color isDark ? #9baec8 : #868c8c + + $bg = isDark ? #282C37 : #fff + + @media (max-width 400px) + padding 16px + + > .signed-in-as + margin-bottom 16px + font-size 14px + color isDark ? #9baec8 : #9daab3 + + > main + margin-bottom 16px + background $bg + border-radius 8px + box-shadow 0 4px 12px rgba(#000, 0.1) + overflow hidden + + > .banner + height 128px + background-position center + background-size cover + + > .avatar + display block + margin -50px auto 0 auto + width 100px + height 100px + border-radius 100% + border solid 4px $bg + + > .body + padding 4px 32px 32px 32px + + @media (max-width 400px) + padding 4px 16px 16px 16px + + > .name + font-size 20px + font-weight bold + + > .username + display block + opacity 0.7 + + > .description + margin-top 16px + + > button + display block + user-select none + cursor pointer + padding 10px 16px + margin 0 + width 100% + min-width 150px + font-size 14px + font-weight bold + color $theme-color + background transparent + outline none + border solid 1px $theme-color + border-radius 36px + + &:hover + background rgba($theme-color, 0.1) + + &:active + background rgba($theme-color, 0.2) + + &.active + color $theme-color-foreground + background $theme-color + + &:hover + background lighten($theme-color, 10%) + border-color lighten($theme-color, 10%) + + &:active + background darken($theme-color, 10%) + border-color darken($theme-color, 10%) + + &.wait + cursor wait !important + opacity 0.7 + + * + pointer-events none + +.syxhndwprovvuqhmyvveewmbqayniwkv[data-darkmode] + root(true) + +.syxhndwprovvuqhmyvveewmbqayniwkv:not([data-darkmode]) + root(false) + +</style> diff --git a/src/client/app/common/views/widgets/hashtags.vue b/src/client/app/common/views/widgets/hashtags.vue index 9ab855d927..2065bd407c 100644 --- a/src/client/app/common/views/widgets/hashtags.vue +++ b/src/client/app/common/views/widgets/hashtags.vue @@ -6,7 +6,9 @@ <div class="mkw-hashtags--body" :data-mobile="platform == 'mobile'"> <p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p> <p class="empty" v-else-if="stats.length == 0">%fa:exclamation-circle%%i18n:@empty%</p> - <transition-group v-else tag="div" name="chart"> + <!-- トランジションを有効にするとなぜかメモリリークする --> + <!-- <transition-group v-else tag="div" name="chart"> --> + <div> <div v-for="stat in stats" :key="stat.tag"> <div class="tag"> <router-link :to="`/tags/${ stat.tag }`" :title="stat.tag">#{{ stat.tag }}</router-link> @@ -14,7 +16,8 @@ </div> <x-chart class="chart" :src="stat.chart"/> </div> - </transition-group> + </div> + <!-- </transition-group> --> </div> </mk-widget-container> </div> |