summaryrefslogtreecommitdiff
path: root/src/client/app/common/views/components
diff options
context:
space:
mode:
authorsyuilo <syuilotan@yahoo.co.jp>2018-09-04 02:09:56 +0900
committersyuilo <syuilotan@yahoo.co.jp>2018-09-04 02:09:56 +0900
commit3698c679e23c184e897d86e9d75dfe2a110a282c (patch)
treef17584778c11ecd4055d350f99eb4be76b6a1f15 /src/client/app/common/views/components
parent8.23.0 (diff)
downloadmisskey-3698c679e23c184e897d86e9d75dfe2a110a282c.tar.gz
misskey-3698c679e23c184e897d86e9d75dfe2a110a282c.tar.bz2
misskey-3698c679e23c184e897d86e9d75dfe2a110a282c.zip
:pizza:
Diffstat (limited to 'src/client/app/common/views/components')
-rw-r--r--src/client/app/common/views/components/index.ts2
-rw-r--r--src/client/app/common/views/components/trends.chart.vue89
-rw-r--r--src/client/app/common/views/components/trends.vue105
3 files changed, 196 insertions, 0 deletions
diff --git a/src/client/app/common/views/components/index.ts b/src/client/app/common/views/components/index.ts
index 422a3da050..4700b6269e 100644
--- a/src/client/app/common/views/components/index.ts
+++ b/src/client/app/common/views/components/index.ts
@@ -1,5 +1,6 @@
import Vue from 'vue';
+import trends from './trends.vue';
import analogClock from './analog-clock.vue';
import menu from './menu.vue';
import noteHeader from './note-header.vue';
@@ -40,6 +41,7 @@ import uiSelect from './ui/select.vue';
import formButton from './ui/form/button.vue';
import formRadio from './ui/form/radio.vue';
+Vue.component('mk-trends', trends);
Vue.component('mk-analog-clock', analogClock);
Vue.component('mk-menu', menu);
Vue.component('mk-note-header', noteHeader);
diff --git a/src/client/app/common/views/components/trends.chart.vue b/src/client/app/common/views/components/trends.chart.vue
new file mode 100644
index 0000000000..723a3947f8
--- /dev/null
+++ b/src/client/app/common/views/components/trends.chart.vue
@@ -0,0 +1,89 @@
+<template>
+<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`" style="overflow:visible">
+ <defs>
+ <linearGradient :id="gradientId" x1="0" x2="0" y1="1" y2="0">
+ <stop offset="0%" stop-color="hsl(200, 80%, 70%)"></stop>
+ <stop offset="100%" stop-color="hsl(90, 80%, 70%)"></stop>
+ </linearGradient>
+ <mask :id="maskId" x="0" y="0" :width="viewBoxX" :height="viewBoxY">
+ <polygon
+ :points="polygonPoints"
+ fill="#fff"
+ fill-opacity="0.5"/>
+ <polyline
+ :points="polylinePoints"
+ fill="none"
+ stroke="#fff"
+ stroke-width="2"/>
+ <circle
+ :cx="headX"
+ :cy="headY"
+ r="3"
+ fill="#fff"/>
+ </mask>
+ </defs>
+ <rect
+ x="-10" y="-10"
+ :width="viewBoxX + 20" :height="viewBoxY + 20"
+ :style="`stroke: none; fill: url(#${ gradientId }); mask: url(#${ maskId })`"/>
+</svg>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+import * as uuid from 'uuid';
+
+export default Vue.extend({
+ props: {
+ src: {
+ type: Array,
+ required: true
+ }
+ },
+ data() {
+ return {
+ viewBoxX: 50,
+ viewBoxY: 30,
+ gradientId: uuid(),
+ maskId: uuid(),
+ polylinePoints: '',
+ polygonPoints: '',
+ headX: null,
+ headY: null,
+ clock: null
+ };
+ },
+ watch: {
+ src() {
+ this.draw();
+ }
+ },
+ created() {
+ this.draw();
+
+ // Vueが何故かWatchを発動させない場合があるので
+ this.clock = setInterval(this.draw, 1000);
+ },
+ beforeDestroy() {
+ clearInterval(this.clock);
+ },
+ methods: {
+ draw() {
+ const stats = this.src.slice().reverse();
+ const peak = Math.max.apply(null, stats) || 1;
+
+ const polylinePoints = stats.map((n, i) => [
+ i * (this.viewBoxX / (stats.length - 1)),
+ (1 - (n / peak)) * this.viewBoxY
+ ]);
+
+ this.polylinePoints = polylinePoints.map(xy => `${xy[0]},${xy[1]}`).join(' ');
+
+ this.polygonPoints = `0,${ this.viewBoxY } ${ this.polylinePoints } ${ this.viewBoxX },${ this.viewBoxY }`;
+
+ this.headX = polylinePoints[polylinePoints.length - 1][0];
+ this.headY = polylinePoints[polylinePoints.length - 1][1];
+ }
+ }
+});
+</script>
diff --git a/src/client/app/common/views/components/trends.vue b/src/client/app/common/views/components/trends.vue
new file mode 100644
index 0000000000..627edc3876
--- /dev/null
+++ b/src/client/app/common/views/components/trends.vue
@@ -0,0 +1,105 @@
+<template>
+<div class="csqvmxybqbycalfhkxvyfrgbrdalkaoc">
+ <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"> -->
+ <div>
+ <div v-for="stat in stats" :key="stat.tag">
+ <div class="tag">
+ <router-link :to="`/tags/${ encodeURIComponent(stat.tag) }`" :title="stat.tag">#{{ stat.tag }}</router-link>
+ <p>{{ '%i18n:@count%'.replace('{}', stat.usersCount) }}</p>
+ </div>
+ <x-chart class="chart" :src="stat.chart"/>
+ </div>
+ </div>
+ <!-- </transition-group> -->
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+import XChart from './trends.chart.vue';
+
+export default Vue.extend({
+ components: {
+ XChart
+ },
+ data() {
+ return {
+ stats: [],
+ fetching: true,
+ clock: null
+ };
+ },
+ mounted() {
+ this.fetch();
+ this.clock = setInterval(this.fetch, 1000 * 60);
+ },
+ beforeDestroy() {
+ clearInterval(this.clock);
+ },
+ methods: {
+ fetch() {
+ (this as any).api('hashtags/trend').then(stats => {
+ this.stats = stats;
+ this.fetching = false;
+ });
+ }
+ }
+});
+</script>
+
+<style lang="stylus" scoped>
+root(isDark)
+ > .fetching
+ > .empty
+ margin 0
+ padding 16px
+ text-align center
+ color #aaa
+
+ > [data-fa]
+ margin-right 4px
+
+ > div
+ .chart-move
+ transition transform 1s ease
+
+ > div
+ display flex
+ align-items center
+ padding 14px 16px
+
+ &:not(:last-child)
+ border-bottom solid 1px isDark ? #393f4f : #eee
+
+ > .tag
+ flex 1
+ overflow hidden
+ font-size 14px
+ color isDark ? #9baec8 : #65727b
+
+ > a
+ display block
+ width 100%
+ white-space nowrap
+ overflow hidden
+ text-overflow ellipsis
+ color inherit
+
+ > p
+ margin 0
+ font-size 75%
+ opacity 0.7
+
+ > .chart
+ height 30px
+
+.csqvmxybqbycalfhkxvyfrgbrdalkaoc[data-darkmode]
+ root(true)
+
+.csqvmxybqbycalfhkxvyfrgbrdalkaoc:not([data-darkmode])
+ root(false)
+
+</style>