summaryrefslogtreecommitdiff
path: root/src/web/app/desktop/views/components/widgets
diff options
context:
space:
mode:
authorsyuilo <syuilotan@yahoo.co.jp>2018-02-18 23:51:41 +0900
committersyuilo <syuilotan@yahoo.co.jp>2018-02-18 23:51:41 +0900
commite01b9d3f16dacb6504c69a65b2f3c3e0f85c4094 (patch)
treec503ed5aea35864ee8d46804039727cf309e4a67 /src/web/app/desktop/views/components/widgets
parentwip (diff)
downloadmisskey-e01b9d3f16dacb6504c69a65b2f3c3e0f85c4094.tar.gz
misskey-e01b9d3f16dacb6504c69a65b2f3c3e0f85c4094.tar.bz2
misskey-e01b9d3f16dacb6504c69a65b2f3c3e0f85c4094.zip
wip
Diffstat (limited to 'src/web/app/desktop/views/components/widgets')
-rw-r--r--src/web/app/desktop/views/components/widgets/calendar.vue192
-rw-r--r--src/web/app/desktop/views/components/widgets/donation.vue45
-rw-r--r--src/web/app/desktop/views/components/widgets/messaging.vue2
-rw-r--r--src/web/app/desktop/views/components/widgets/nav.vue29
-rw-r--r--src/web/app/desktop/views/components/widgets/notifications.vue70
-rw-r--r--src/web/app/desktop/views/components/widgets/photo-stream.vue122
-rw-r--r--src/web/app/desktop/views/components/widgets/profile.vue125
-rw-r--r--src/web/app/desktop/views/components/widgets/slideshow.vue153
-rw-r--r--src/web/app/desktop/views/components/widgets/tips.vue108
9 files changed, 845 insertions, 1 deletions
diff --git a/src/web/app/desktop/views/components/widgets/calendar.vue b/src/web/app/desktop/views/components/widgets/calendar.vue
new file mode 100644
index 0000000000..8574bf59f9
--- /dev/null
+++ b/src/web/app/desktop/views/components/widgets/calendar.vue
@@ -0,0 +1,192 @@
+<template>
+<div class="mkw-calendar"
+ :data-melt="props.design == 1"
+ :data-special="special"
+>
+ <div class="calendar" :data-is-holiday="isHoliday">
+ <p class="month-and-year">
+ <span class="year">{{ year }}年</span>
+ <span class="month">{{ month }}月</span>
+ </p>
+ <p class="day">{{ day }}日</p>
+ <p class="week-day">{{ weekDay }}曜日</p>
+ </div>
+ <div class="info">
+ <div>
+ <p>今日:<b>{{ dayP.toFixed(1) }}%</b></p>
+ <div class="meter">
+ <div class="val" :style="{ width: `${dayP}%` }"></div>
+ </div>
+ </div>
+ <div>
+ <p>今月:<b>{{ monthP.toFixed(1) }}%</b></p>
+ <div class="meter">
+ <div class="val" :style="{ width: `${monthP}%` }"></div>
+ </div>
+ </div>
+ <div>
+ <p>今年:<b>{{ yearP.toFixed(1) }}%</b></p>
+ <div class="meter">
+ <div class="val" :style="{ width: `${yearP}%` }"></div>
+ </div>
+ </div>
+ </div>
+</div>
+</template>
+
+<script lang="ts">
+import define from '../../../../common/define-widget';
+export default define({
+ name: 'calendar',
+ props: {
+ design: 0
+ }
+}).extend({
+ data() {
+ return {
+ now: new Date(),
+ year: null,
+ month: null,
+ day: null,
+ weekDay: null,
+ yearP: null,
+ dayP: null,
+ monthP: null,
+ isHoliday: null,
+ special: null,
+ clock: null
+ };
+ },
+ created() {
+ this.tick();
+ this.clock = setInterval(this.tick, 1000);
+ },
+ beforeDestroy() {
+ clearInterval(this.clock);
+ },
+ methods: {
+ func() {
+ if (this.props.design == 2) {
+ this.props.design = 0;
+ } else {
+ this.props.design++;
+ }
+ },
+ tick() {
+ const now = new Date();
+ const nd = now.getDate();
+ const nm = now.getMonth();
+ const ny = now.getFullYear();
+
+ this.year = ny;
+ this.month = nm + 1;
+ this.day = nd;
+ this.weekDay = ['日', '月', '火', '水', '木', '金', '土'][now.getDay()];
+
+ const dayNumer = now.getTime() - new Date(ny, nm, nd).getTime();
+ const dayDenom = 1000/*ms*/ * 60/*s*/ * 60/*m*/ * 24/*h*/;
+ const monthNumer = now.getTime() - new Date(ny, nm, 1).getTime();
+ const monthDenom = new Date(ny, nm + 1, 1).getTime() - new Date(ny, nm, 1).getTime();
+ const yearNumer = now.getTime() - new Date(ny, 0, 1).getTime();
+ const yearDenom = new Date(ny + 1, 0, 1).getTime() - new Date(ny, 0, 1).getTime();
+
+ this.dayP = dayNumer / dayDenom * 100;
+ this.monthP = monthNumer / monthDenom * 100;
+ this.yearP = yearNumer / yearDenom * 100;
+
+ this.isHoliday = now.getDay() == 0 || now.getDay() == 6;
+
+ this.special =
+ nm == 0 && nd == 1 ? 'on-new-years-day' :
+ false;
+ }
+ }
+});
+</script>
+
+<style lang="stylus" scoped>
+.mkw-calendar
+ padding 16px 0
+ color #777
+ background #fff
+ border solid 1px rgba(0, 0, 0, 0.075)
+ border-radius 6px
+
+ &[data-special='on-new-years-day']
+ border-color #ef95a0
+
+ &[data-melt]
+ background transparent
+ border none
+
+ &:after
+ content ""
+ display block
+ clear both
+
+ > .calendar
+ float left
+ width 60%
+ text-align center
+
+ &[data-is-holiday]
+ > .day
+ color #ef95a0
+
+ > p
+ margin 0
+ line-height 18px
+ font-size 14px
+
+ > span
+ margin 0 4px
+
+ > .day
+ margin 10px 0
+ line-height 32px
+ font-size 28px
+
+ > .info
+ display block
+ float left
+ width 40%
+ padding 0 16px 0 0
+
+ > div
+ margin-bottom 8px
+
+ &:last-child
+ margin-bottom 4px
+
+ > p
+ margin 0 0 2px 0
+ font-size 12px
+ line-height 18px
+ color #888
+
+ > b
+ margin-left 2px
+
+ > .meter
+ width 100%
+ overflow hidden
+ background #eee
+ border-radius 8px
+
+ > .val
+ height 4px
+ background $theme-color
+
+ &:nth-child(1)
+ > .meter > .val
+ background #f7796c
+
+ &:nth-child(2)
+ > .meter > .val
+ background #a1de41
+
+ &:nth-child(3)
+ > .meter > .val
+ background #41ddde
+
+</style>
diff --git a/src/web/app/desktop/views/components/widgets/donation.vue b/src/web/app/desktop/views/components/widgets/donation.vue
new file mode 100644
index 0000000000..b3e0658a4b
--- /dev/null
+++ b/src/web/app/desktop/views/components/widgets/donation.vue
@@ -0,0 +1,45 @@
+<template>
+<div class="mkw-donation">
+ <article>
+ <h1>%fa:heart%%i18n:desktop.tags.mk-donation-home-widget.title%</h1>
+ <p>
+ {{ '%i18n:desktop.tags.mk-donation-home-widget.text%'.substr(0, '%i18n:desktop.tags.mk-donation-home-widget.text%'.indexOf('{')) }}
+ <a href="/syuilo" data-user-preview="@syuilo">@syuilo</a>
+ {{ '%i18n:desktop.tags.mk-donation-home-widget.text%'.substr('%i18n:desktop.tags.mk-donation-home-widget.text%'.indexOf('}') + 1) }}
+ </p>
+ </article>
+</div>
+</template>
+
+<script lang="ts">
+import define from '../../../../common/define-widget';
+export default define({
+ name: 'donation'
+});
+</script>
+
+<style lang="stylus" scoped>
+.mkw-donation
+ background #fff
+ border solid 1px #ead8bb
+ border-radius 6px
+
+ > article
+ padding 20px
+
+ > h1
+ margin 0 0 5px 0
+ font-size 1em
+ color #888
+
+ > [data-fa]
+ margin-right 0.25em
+
+ > p
+ display block
+ z-index 1
+ margin 0
+ font-size 0.8em
+ color #999
+
+</style>
diff --git a/src/web/app/desktop/views/components/widgets/messaging.vue b/src/web/app/desktop/views/components/widgets/messaging.vue
index f31acc5c63..733989b782 100644
--- a/src/web/app/desktop/views/components/widgets/messaging.vue
+++ b/src/web/app/desktop/views/components/widgets/messaging.vue
@@ -6,7 +6,7 @@
</template>
<script lang="ts">
-import define from '../../../define-widget';
+import define from '../../../../common/define-widget';
export default define({
name: 'messaging',
props: {
diff --git a/src/web/app/desktop/views/components/widgets/nav.vue b/src/web/app/desktop/views/components/widgets/nav.vue
new file mode 100644
index 0000000000..a782ad62bf
--- /dev/null
+++ b/src/web/app/desktop/views/components/widgets/nav.vue
@@ -0,0 +1,29 @@
+<template>
+<div class="mkw-nav">
+ <mk-nav-links/>
+</div>
+</template>
+
+<script lang="ts">
+import define from '../../../../common/define-widget';
+export default define({
+ name: 'nav'
+});
+</script>
+
+<style lang="stylus" scoped>
+.mkw-nav
+ padding 16px
+ font-size 12px
+ color #aaa
+ background #fff
+ border solid 1px rgba(0, 0, 0, 0.075)
+ border-radius 6px
+
+ a
+ color #999
+
+ i
+ color #ccc
+
+</style>
diff --git a/src/web/app/desktop/views/components/widgets/notifications.vue b/src/web/app/desktop/views/components/widgets/notifications.vue
new file mode 100644
index 0000000000..2d613fa232
--- /dev/null
+++ b/src/web/app/desktop/views/components/widgets/notifications.vue
@@ -0,0 +1,70 @@
+<template>
+<div class="mkw-notifications">
+ <template v-if="!props.compact">
+ <p class="title">%fa:R bell%%i18n:desktop.tags.mk-notifications-home-widget.title%</p>
+ <button @click="settings" title="%i18n:desktop.tags.mk-notifications-home-widget.settings%">%fa:cog%</button>
+ </template>
+ <mk-notifications/>
+</div>
+</template>
+
+<script lang="ts">
+import define from '../../../../common/define-widget';
+export default define({
+ name: 'notifications',
+ props: {
+ compact: false
+ }
+}).extend({
+ methods: {
+ settings() {
+ alert('not implemented yet');
+ },
+ func() {
+ this.props.compact = !this.props.compact;
+ }
+ }
+});
+</script>
+
+<style lang="stylus" scoped>
+.mkw-notifications
+ background #fff
+ border solid 1px rgba(0, 0, 0, 0.075)
+ border-radius 6px
+
+ > .title
+ z-index 1
+ margin 0
+ padding 0 16px
+ line-height 42px
+ font-size 0.9em
+ font-weight bold
+ color #888
+ box-shadow 0 1px rgba(0, 0, 0, 0.07)
+
+ > [data-fa]
+ margin-right 4px
+
+ > button
+ position absolute
+ z-index 2
+ top 0
+ right 0
+ padding 0
+ width 42px
+ font-size 0.9em
+ line-height 42px
+ color #ccc
+
+ &:hover
+ color #aaa
+
+ &:active
+ color #999
+
+ > .mk-notifications
+ max-height 300px
+ overflow auto
+
+</style>
diff --git a/src/web/app/desktop/views/components/widgets/photo-stream.vue b/src/web/app/desktop/views/components/widgets/photo-stream.vue
new file mode 100644
index 0000000000..a3f37e8c7e
--- /dev/null
+++ b/src/web/app/desktop/views/components/widgets/photo-stream.vue
@@ -0,0 +1,122 @@
+<template>
+<div class="mkw-photo-stream" :data-melt="props.design == 2">
+ <p class="title" v-if="props.design == 0">%fa:camera%%i18n:desktop.tags.mk-photo-stream-home-widget.title%</p>
+ <p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
+ <div class="stream" v-if="!fetching && images.length > 0">
+ <div v-for="image in images" :key="image.id" class="img" :style="`background-image: url(${image.url}?thumbnail&size=256)`"></div>
+ </div>
+ <p class="empty" v-if="!fetching && images.length == 0">%i18n:desktop.tags.mk-photo-stream-home-widget.no-photos%</p>
+</div>
+</template>
+
+<script lang="ts">
+import define from '../../../../common/define-widget';
+export default define({
+ name: 'photo-stream',
+ props: {
+ design: 0
+ }
+}).extend({
+ data() {
+ return {
+ images: [],
+ fetching: true,
+ connection: null,
+ connectionId: null
+ };
+ },
+ mounted() {
+ this.connection = (this as any).os.stream.getConnection();
+ this.connectionId = (this as any).os.stream.use();
+
+ this.connection.on('drive_file_created', this.onDriveFileCreated);
+
+ (this as any).api('drive/stream', {
+ type: 'image/*',
+ limit: 9
+ }).then(images => {
+ this.fetching = false;
+ this.images = images;
+ });
+ },
+ beforeDestroy() {
+ this.connection.off('drive_file_created', this.onDriveFileCreated);
+ (this as any).os.stream.dispose(this.connectionId);
+ },
+ methods: {
+ onDriveFileCreated(file) {
+ if (/^image\/.+$/.test(file.type)) {
+ this.images.unshift(file);
+ if (this.images.length > 9) this.images.pop();
+ }
+ },
+ func() {
+ if (this.props.design == 2) {
+ this.props.design = 0;
+ } else {
+ this.props.design++;
+ }
+ }
+ }
+});
+</script>
+
+<style lang="stylus" scoped>
+.mkw-photo-stream
+ background #fff
+ border solid 1px rgba(0, 0, 0, 0.075)
+ border-radius 6px
+
+ &[data-melt]
+ background transparent !important
+ border none !important
+
+ > .stream
+ padding 0
+
+ > .img
+ border solid 4px transparent
+ border-radius 8px
+
+ > .title
+ z-index 1
+ margin 0
+ padding 0 16px
+ line-height 42px
+ font-size 0.9em
+ font-weight bold
+ color #888
+ box-shadow 0 1px rgba(0, 0, 0, 0.07)
+
+ > [data-fa]
+ margin-right 4px
+
+ > .stream
+ display -webkit-flex
+ display -moz-flex
+ display -ms-flex
+ display flex
+ justify-content center
+ flex-wrap wrap
+ padding 8px
+
+ > .img
+ flex 1 1 33%
+ width 33%
+ height 80px
+ background-position center center
+ background-size cover
+ border solid 2px transparent
+ border-radius 4px
+
+ > .fetching
+ > .empty
+ margin 0
+ padding 16px
+ text-align center
+ color #aaa
+
+ > [data-fa]
+ margin-right 4px
+
+</style>
diff --git a/src/web/app/desktop/views/components/widgets/profile.vue b/src/web/app/desktop/views/components/widgets/profile.vue
new file mode 100644
index 0000000000..9a0d40a5c0
--- /dev/null
+++ b/src/web/app/desktop/views/components/widgets/profile.vue
@@ -0,0 +1,125 @@
+<template>
+<div class="mkw-profile"
+ :data-compact="props.design == 1 || props.design == 2"
+ :data-melt="props.design == 2"
+>
+ <div class="banner"
+ style={ I.banner_url ? 'background-image: url(' + I.banner_url + '?thumbnail&size=256)' : '' }
+ title="クリックでバナー編集"
+ @click="wapi_setBanner"
+ ></div>
+ <img class="avatar"
+ src={ I.avatar_url + '?thumbnail&size=96' }
+ @click="wapi_setAvatar"
+ alt="avatar"
+ title="クリックでアバター編集"
+ v-user-preview={ I.id }
+ />
+ <a class="name" href={ '/' + I.username }>{ I.name }</a>
+ <p class="username">@{ I.username }</p>
+</div>
+</template>
+
+<script lang="ts">
+import define from '../../../../common/define-widget';
+export default define({
+ name: 'profile',
+ props: {
+ design: 0
+ }
+}).extend({
+ methods: {
+ func() {
+ if (this.props.design == 2) {
+ this.props.design = 0;
+ } else {
+ this.props.design++;
+ }
+ }
+ }
+});
+</script>
+
+<style lang="stylus" scoped>
+.mkw-profile
+ overflow hidden
+ background #fff
+ border solid 1px rgba(0, 0, 0, 0.075)
+ border-radius 6px
+
+ &[data-compact]
+ > .banner:before
+ content ""
+ display block
+ width 100%
+ height 100%
+ background rgba(0, 0, 0, 0.5)
+
+ > .avatar
+ top ((100px - 58px) / 2)
+ left ((100px - 58px) / 2)
+ border none
+ border-radius 100%
+ box-shadow 0 0 16px rgba(0, 0, 0, 0.5)
+
+ > .name
+ position absolute
+ top 0
+ left 92px
+ margin 0
+ line-height 100px
+ color #fff
+ text-shadow 0 0 8px rgba(0, 0, 0, 0.5)
+
+ > .username
+ display none
+
+ &[data-melt]
+ background transparent !important
+ border none !important
+
+ > .banner
+ visibility hidden
+
+ > .avatar
+ box-shadow none
+
+ > .name
+ color #666
+ text-shadow none
+
+ > .banner
+ height 100px
+ background-color #f5f5f5
+ background-size cover
+ background-position center
+ cursor pointer
+
+ > .avatar
+ display block
+ position absolute
+ top 76px
+ left 16px
+ width 58px
+ height 58px
+ margin 0
+ border solid 3px #fff
+ border-radius 8px
+ vertical-align bottom
+ cursor pointer
+
+ > .name
+ display block
+ margin 10px 0 0 84px
+ line-height 16px
+ font-weight bold
+ color #555
+
+ > .username
+ display block
+ margin 4px 0 8px 84px
+ line-height 16px
+ font-size 0.9em
+ color #999
+
+</style>
diff --git a/src/web/app/desktop/views/components/widgets/slideshow.vue b/src/web/app/desktop/views/components/widgets/slideshow.vue
new file mode 100644
index 0000000000..beda350666
--- /dev/null
+++ b/src/web/app/desktop/views/components/widgets/slideshow.vue
@@ -0,0 +1,153 @@
+<template>
+<div class="mkw-slideshow">
+ <div @click="choose">
+ <p v-if="data.folder === undefined">クリックしてフォルダを指定してください</p>
+ <p v-if="data.folder !== undefined && images.length == 0 && !fetching">このフォルダには画像がありません</p>
+ <div ref="slideA" class="slide a"></div>
+ <div ref="slideB" class="slide b"></div>
+ </div>
+ <button @click="resize">%fa:expand%</button>
+</div>
+</template>
+
+<script lang="ts">
+import * as anime from 'animejs';
+import define from '../../../../common/define-widget';
+export default define({
+ name: 'slideshow',
+ props: {
+ folder: undefined,
+ size: 0
+ }
+}).extend({
+ data() {
+ return {
+ images: [],
+ fetching: true,
+ clock: null
+ };
+ },
+ mounted() {
+ this.$nextTick(() => {
+ this.applySize();
+ });
+
+ if (this.props.folder !== undefined) {
+ this.fetch();
+ }
+
+ this.clock = setInterval(this.change, 10000);
+ },
+ beforeDestroy() {
+ clearInterval(this.clock);
+ },
+ methods: {
+ applySize() {
+ let h;
+
+ if (this.props.size == 1) {
+ h = 250;
+ } else {
+ h = 170;
+ }
+
+ this.$el.style.height = `${h}px`;
+ },
+ resize() {
+ if (this.props.size == 1) {
+ this.props.size = 0;
+ } else {
+ this.props.size++;
+ }
+
+ this.applySize();
+ },
+ change() {
+ if (this.images.length == 0) return;
+
+ const index = Math.floor(Math.random() * this.images.length);
+ const img = `url(${ this.images[index].url }?thumbnail&size=1024)`;
+
+ (this.$refs.slideB as any).style.backgroundImage = img;
+
+ anime({
+ targets: this.$refs.slideB,
+ opacity: 1,
+ duration: 1000,
+ easing: 'linear',
+ complete: () => {
+ (this.$refs.slideA as any).style.backgroundImage = img;
+ anime({
+ targets: this.$refs.slideB,
+ opacity: 0,
+ duration: 0
+ });
+ }
+ });
+ },
+ fetch() {
+ this.fetching = true;
+
+ (this as any).api('drive/files', {
+ folder_id: this.props.folder,
+ type: 'image/*',
+ limit: 100
+ }).then(images => {
+ this.fetching = false;
+ this.images = images;
+ (this.$refs.slideA as any).style.backgroundImage = '';
+ (this.$refs.slideB as any).style.backgroundImage = '';
+ this.change();
+ });
+ },
+ choose() {
+ (this as any).apis.chooseDriveFolder().then(folder => {
+ this.props.folder = folder ? folder.id : null;
+ this.fetch();
+ });
+ }
+ }
+});
+</script>
+
+<style lang="stylus" scoped>
+.mkw-slideshow
+ overflow hidden
+ background #fff
+ border solid 1px rgba(0, 0, 0, 0.075)
+ border-radius 6px
+
+ &:hover > button
+ display block
+
+ > button
+ position absolute
+ left 0
+ bottom 0
+ display none
+ padding 4px
+ font-size 24px
+ color #fff
+ text-shadow 0 0 8px #000
+
+ > div
+ width 100%
+ height 100%
+ cursor pointer
+
+ > *
+ pointer-events none
+
+ > .slide
+ position absolute
+ top 0
+ left 0
+ width 100%
+ height 100%
+ background-size cover
+ background-position center
+
+ &.b
+ opacity 0
+
+</style>
diff --git a/src/web/app/desktop/views/components/widgets/tips.vue b/src/web/app/desktop/views/components/widgets/tips.vue
new file mode 100644
index 0000000000..2991fbc3b9
--- /dev/null
+++ b/src/web/app/desktop/views/components/widgets/tips.vue
@@ -0,0 +1,108 @@
+<template>
+<div class="mkw-tips">
+ <p ref="tip">%fa:R lightbulb%<span v-html="tip"></span></p>
+</div>
+</template>
+
+<script lang="ts">
+import * as anime from 'animejs';
+import define from '../../../../common/define-widget';
+
+const tips = [
+ '<kbd>t</kbd>でタイムラインにフォーカスできます',
+ '<kbd>p</kbd>または<kbd>n</kbd>で投稿フォームを開きます',
+ '投稿フォームにはファイルをドラッグ&ドロップできます',
+ '投稿フォームにクリップボードにある画像データをペーストできます',
+ 'ドライブにファイルをドラッグ&ドロップしてアップロードできます',
+ 'ドライブでファイルをドラッグしてフォルダ移動できます',
+ 'ドライブでフォルダをドラッグしてフォルダ移動できます',
+ 'ホームは設定からカスタマイズできます',
+ 'MisskeyはMIT Licenseです',
+ 'タイムマシンウィジェットを利用すると、簡単に過去のタイムラインに遡れます',
+ '投稿の ... をクリックして、投稿をユーザーページにピン留めできます',
+ 'ドライブの容量は(デフォルトで)1GBです',
+ '投稿に添付したファイルは全てドライブに保存されます',
+ 'ホームのカスタマイズ中、ウィジェットを右クリックしてデザインを変更できます',
+ 'タイムライン上部にもウィジェットを設置できます',
+ '投稿をダブルクリックすると詳細が見れます',
+ '「**」でテキストを囲むと**強調表示**されます',
+ 'チャンネルウィジェットを利用すると、よく利用するチャンネルを素早く確認できます',
+ 'いくつかのウィンドウはブラウザの外に切り離すことができます',
+ 'カレンダーウィジェットのパーセンテージは、経過の割合を示しています',
+ 'APIを利用してbotの開発なども行えます',
+ 'MisskeyはLINEを通じてでも利用できます',
+ 'まゆかわいいよまゆ',
+ 'Misskeyは2014年にサービスを開始しました',
+ '対応ブラウザではMisskeyを開いていなくても通知を受け取れます'
+]
+
+export default define({
+ name: 'tips'
+}).extend({
+ data() {
+ return {
+ tip: null,
+ clock: null
+ };
+ },
+ mounted() {
+ this.$nextTick(() => {
+ this.set();
+ });
+
+ this.clock = setInterval(this.change, 20000);
+ },
+ beforeDestroy() {
+ clearInterval(this.clock);
+ },
+ methods: {
+ set() {
+ this.tip = tips[Math.floor(Math.random() * tips.length)];
+ },
+ change() {
+ anime({
+ targets: this.$refs.tip,
+ opacity: 0,
+ duration: 500,
+ easing: 'linear',
+ complete: this.set
+ });
+
+ setTimeout(() => {
+ anime({
+ targets: this.$refs.tip,
+ opacity: 1,
+ duration: 500,
+ easing: 'linear'
+ });
+ }, 500);
+ }
+ }
+});
+</script>
+
+<style lang="stylus" scoped>
+.mkw-tips
+ overflow visible !important
+
+ > p
+ display block
+ margin 0
+ padding 0 12px
+ text-align center
+ font-size 0.7em
+ color #999
+
+ > [data-fa]
+ margin-right 4px
+
+ kbd
+ display inline
+ padding 0 6px
+ margin 0 2px
+ font-size 1em
+ font-family inherit
+ border solid 1px #999
+ border-radius 2px
+
+</style>