diff options
| author | syuilo <Syuilotan@yahoo.co.jp> | 2020-08-09 15:51:02 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-08-09 15:51:02 +0900 |
| commit | 69d9aa71f25ab06d8024b04ce341909425d053d6 (patch) | |
| tree | 868ac53c1de7ea628a464ab277ee3aa573fdb24b /src/client/components | |
| parent | :v: (diff) | |
| download | misskey-69d9aa71f25ab06d8024b04ce341909425d053d6.tar.gz misskey-69d9aa71f25ab06d8024b04ce341909425d053d6.tar.bz2 misskey-69d9aa71f25ab06d8024b04ce341909425d053d6.zip | |
Full view mode (#6636)
* wuip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* Update folder.vue
* wip
* Update size.ts
* wip
* wip
* Update index.vue
* wip
Diffstat (limited to 'src/client/components')
| -rw-r--r-- | src/client/components/instance-stats.vue | 95 | ||||
| -rw-r--r-- | src/client/components/note.sub.vue | 2 | ||||
| -rw-r--r-- | src/client/components/note.vue | 2 | ||||
| -rw-r--r-- | src/client/components/notification.vue | 2 | ||||
| -rw-r--r-- | src/client/components/tab.vue | 2 | ||||
| -rw-r--r-- | src/client/components/ui/container.vue | 27 | ||||
| -rw-r--r-- | src/client/components/ui/folder.vue | 126 | ||||
| -rw-r--r-- | src/client/components/url-preview.vue | 2 |
8 files changed, 222 insertions, 36 deletions
diff --git a/src/client/components/instance-stats.vue b/src/client/components/instance-stats.vue index 552e3523f7..f863dfd95e 100644 --- a/src/client/components/instance-stats.vue +++ b/src/client/components/instance-stats.vue @@ -1,5 +1,5 @@ <template> -<div class="zbcjwnqg"> +<div class="zbcjwnqg" v-size="{ max: [550, 1200] }"> <div class="stats" v-if="info"> <div class="_panel"> <div> @@ -127,7 +127,6 @@ import { faChartBar, faUser, faPencilAlt } from '@fortawesome/free-solid-svg-ico import Chart from 'chart.js'; import MkSelect from './ui/select.vue'; -const chartLimit = 90; const sum = (...arr) => arr.reduce((r, a) => r.map((b, i) => a[i] + b)); const negate = arr => arr.map(x => -x); const alpha = (hex, a) => { @@ -143,6 +142,19 @@ export default Vue.extend({ MkSelect }, + props: { + chartLimit: { + type: Number, + required: false, + default: 90 + }, + detailed: { + type: Boolean, + required: false, + default: false + }, + }, + data() { return { info: null, @@ -209,17 +221,17 @@ export default Vue.extend({ this.now = new Date(); const [perHour, perDay] = await Promise.all([Promise.all([ - this.$root.api('charts/federation', { limit: chartLimit, span: 'hour' }), - this.$root.api('charts/users', { limit: chartLimit, span: 'hour' }), - this.$root.api('charts/active-users', { limit: chartLimit, span: 'hour' }), - this.$root.api('charts/notes', { limit: chartLimit, span: 'hour' }), - this.$root.api('charts/drive', { limit: chartLimit, span: 'hour' }), + this.$root.api('charts/federation', { limit: this.chartLimit, span: 'hour' }), + this.$root.api('charts/users', { limit: this.chartLimit, span: 'hour' }), + this.$root.api('charts/active-users', { limit: this.chartLimit, span: 'hour' }), + this.$root.api('charts/notes', { limit: this.chartLimit, span: 'hour' }), + this.$root.api('charts/drive', { limit: this.chartLimit, span: 'hour' }), ]), Promise.all([ - this.$root.api('charts/federation', { limit: chartLimit, span: 'day' }), - this.$root.api('charts/users', { limit: chartLimit, span: 'day' }), - this.$root.api('charts/active-users', { limit: chartLimit, span: 'day' }), - this.$root.api('charts/notes', { limit: chartLimit, span: 'day' }), - this.$root.api('charts/drive', { limit: chartLimit, span: 'day' }), + this.$root.api('charts/federation', { limit: this.chartLimit, span: 'day' }), + this.$root.api('charts/users', { limit: this.chartLimit, span: 'day' }), + this.$root.api('charts/active-users', { limit: this.chartLimit, span: 'day' }), + this.$root.api('charts/notes', { limit: this.chartLimit, span: 'day' }), + this.$root.api('charts/drive', { limit: this.chartLimit, span: 'day' }), ])]); const chart = { @@ -259,11 +271,14 @@ export default Vue.extend({ this.chartInstance.destroy(); } + // TODO: var(--panel)の色が暗いか明るいかで判定する + const gridColor = this.$store.state.device.darkMode ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)'; + Chart.defaults.global.defaultFontColor = getComputedStyle(document.documentElement).getPropertyValue('--fg'); this.chartInstance = new Chart(this.$refs.chart, { type: 'line', data: { - labels: new Array(chartLimit).fill(0).map((_, i) => this.getDate(i).toLocaleString()).slice().reverse(), + labels: new Array(this.chartLimit).fill(0).map((_, i) => this.getDate(i).toLocaleString()).slice().reverse(), datasets: this.data.series.map(x => ({ label: x.name, data: x.data.slice().reverse(), @@ -271,6 +286,7 @@ export default Vue.extend({ lineTension: 0, borderWidth: 2, borderColor: x.color, + borderDash: x.borderDash || [], backgroundColor: alpha(x.color, 0.1), hidden: !!x.hidden })) @@ -293,17 +309,28 @@ export default Vue.extend({ }, scales: { xAxes: [{ + type: 'time', + time: { + stepSize: 1, + unit: this.chartSpan == 'day' ? 'month' : 'day', + }, gridLines: { - display: false + display: this.detailed, + color: gridColor, + zeroLineColor: gridColor, }, ticks: { - display: false + display: this.detailed } }], yAxes: [{ - position: 'right', + position: 'left', + gridLines: { + color: gridColor, + zeroLineColor: gridColor, + }, ticks: { - display: false + display: this.detailed } }] }, @@ -325,7 +352,11 @@ export default Vue.extend({ }, format(arr) { - return arr; + const now = Date.now(); + return arr.map((v, i) => ({ + x: new Date(now - ((this.chartSpan == 'day' ? 86400000 :3600000 ) * i)), + y: v + })); }, federationInstancesChart(total: boolean): any { @@ -347,6 +378,7 @@ export default Vue.extend({ name: 'All', type: 'line', color: '#008FFB', + borderDash: [5, 5], data: this.format(type == 'combined' ? sum(this.stats.notes.local.inc, negate(this.stats.notes.local.dec), this.stats.notes.remote.inc, negate(this.stats.notes.remote.dec)) : sum(this.stats.notes[type].inc, negate(this.stats.notes[type].dec)) @@ -586,17 +618,30 @@ export default Vue.extend({ <style lang="scss" scoped> .zbcjwnqg { + &.max-width_1200px { + > .stats { + grid-template-columns: 1fr 1fr; + grid-template-rows: 1fr 1fr; + } + } + + &.max-width_550px { + > .stats { + grid-template-columns: 1fr; + grid-template-rows: 1fr 1fr 1fr 1fr; + } + } + > .stats { - display: flex; - justify-content: space-between; - flex-wrap: wrap; - margin: calc(0px - var(--margin) / 2); - margin-bottom: calc(var(--margin) / 2); + display: grid; + grid-template-columns: 1fr 1fr 1fr 1fr; + grid-template-rows: 1fr; + gap: var(--margin); + margin-bottom: var(--margin); + font-size: 90%; > div { display: flex; - flex: 1 0 213px; - margin: calc(var(--margin) / 2); box-sizing: border-box; padding: 16px 20px; diff --git a/src/client/components/note.sub.vue b/src/client/components/note.sub.vue index fcdb06bd9e..329fb62d53 100644 --- a/src/client/components/note.sub.vue +++ b/src/client/components/note.sub.vue @@ -1,5 +1,5 @@ <template> -<div class="wrpstxzv" :class="{ children }" v-size="[{ max: 450 }]"> +<div class="wrpstxzv" :class="{ children }" v-size="{ max: [450] }"> <div class="main"> <mk-avatar class="avatar" :user="note.user"/> <div class="body"> diff --git a/src/client/components/note.vue b/src/client/components/note.vue index 322b634218..99a088b3e0 100644 --- a/src/client/components/note.vue +++ b/src/client/components/note.vue @@ -6,7 +6,7 @@ :tabindex="!isDeleted ? '-1' : null" :class="{ renote: isRenote }" v-hotkey="keymap" - v-size="[{ max: 500 }, { max: 450 }, { max: 350 }, { max: 300 }]" + v-size="{ max: [500, 450, 350, 300] }" > <x-sub v-for="note in conversation" class="reply-to-more" :key="note.id" :note="note"/> <x-sub :note="appearNote.reply" class="reply-to" v-if="appearNote.reply"/> diff --git a/src/client/components/notification.vue b/src/client/components/notification.vue index 9e4806f05f..71ac963a58 100644 --- a/src/client/components/notification.vue +++ b/src/client/components/notification.vue @@ -1,5 +1,5 @@ <template> -<div class="qglefbjs" :class="notification.type" v-size="[{ max: 500 }, { max: 600 }]"> +<div class="qglefbjs" :class="notification.type" v-size="{ max: [500, 600] }"> <div class="head"> <mk-avatar v-if="notification.user" class="icon" :user="notification.user"/> <img v-else class="icon" :src="notification.icon" alt=""/> diff --git a/src/client/components/tab.vue b/src/client/components/tab.vue index 824f150840..a10c7fdbf3 100644 --- a/src/client/components/tab.vue +++ b/src/client/components/tab.vue @@ -1,5 +1,5 @@ <template> -<div class="pxhvhrfw" v-size="[{ max: 500 }]"> +<div class="pxhvhrfw" v-size="{ max: [500] }"> <button v-for="item in items" class="_button" @click="$emit('input', item.value)" :class="{ active: value === item.value }" :key="item.value"><fa v-if="item.icon" :icon="item.icon" class="icon"/>{{ item.label }}</button> </div> </template> diff --git a/src/client/components/ui/container.vue b/src/client/components/ui/container.vue index e5b11770af..2487ffd424 100644 --- a/src/client/components/ui/container.vue +++ b/src/client/components/ui/container.vue @@ -1,5 +1,5 @@ <template> -<div class="ukygtjoj _panel" :class="{ naked, hideHeader: !showHeader, scrollable }" v-size="[{ max: 500 }]"> +<div class="ukygtjoj _panel" :class="{ naked, hideHeader: !showHeader, scrollable }" v-size="{ max: [380], el: resizeBaseEl }"> <header v-if="showHeader"> <div class="title"><slot name="header"></slot></div> <slot name="func"></slot> @@ -52,6 +52,9 @@ export default Vue.extend({ required: false, default: false }, + resizeBaseEl: { + required: false, + }, }, data() { return { @@ -103,10 +106,6 @@ export default Vue.extend({ position: relative; overflow: hidden; - & + .ukygtjoj { - margin-top: var(--margin); - } - &.naked { background: transparent !important; box-shadow: none !important; @@ -152,12 +151,28 @@ export default Vue.extend({ } } - &.max-width_500px { + > div { + > ::v-deep ._content { + padding: 24px; + + & + ._content { + border-top: solid 1px var(--divider); + } + } + } + + &.max-width_380px { > header { > .title { padding: 8px 10px; } } + + > div { + > ::v-deep ._content { + padding: 16px; + } + } } } diff --git a/src/client/components/ui/folder.vue b/src/client/components/ui/folder.vue new file mode 100644 index 0000000000..0b489fe9ad --- /dev/null +++ b/src/client/components/ui/folder.vue @@ -0,0 +1,126 @@ +<template> +<div class="ssazuxis" v-size="{ max: [500] }"> + <header @click="() => showBody = !showBody" class="_button"> + <div class="title"><slot name="header"></slot></div> + <div class="divider"></div> + <button class="_button"> + <template v-if="showBody"><fa :icon="faAngleUp"/></template> + <template v-else><fa :icon="faAngleDown"/></template> + </button> + </header> + <transition name="folder-toggle" + @enter="enter" + @after-enter="afterEnter" + @leave="leave" + @after-leave="afterLeave" + > + <div v-show="showBody"> + <slot></slot> + </div> + </transition> +</div> +</template> + +<script lang="ts"> +import Vue from 'vue'; +import { faAngleUp, faAngleDown } from '@fortawesome/free-solid-svg-icons'; + +export default Vue.extend({ + props: { + expanded: { + type: Boolean, + required: false, + default: true + }, + }, + data() { + return { + showBody: this.expanded, + faAngleUp, faAngleDown + }; + }, + methods: { + toggleContent(show: boolean) { + this.showBody = show; + }, + + enter(el) { + const elementHeight = el.getBoundingClientRect().height; + el.style.height = 0; + el.offsetHeight; // reflow + el.style.height = elementHeight + 'px'; + }, + afterEnter(el) { + el.style.height = null; + }, + leave(el) { + const elementHeight = el.getBoundingClientRect().height; + el.style.height = elementHeight + 'px'; + el.offsetHeight; // reflow + el.style.height = 0; + }, + afterLeave(el) { + el.style.height = null; + }, + } +}); +</script> + +<style lang="scss" scoped> +.folder-toggle-enter-active, .folder-toggle-leave-active { + overflow-y: hidden; + transition: opacity 0.5s, height 0.5s !important; +} +.folder-toggle-enter { + opacity: 0; +} +.folder-toggle-leave-to { + opacity: 0; +} + +.ssazuxis { + position: relative; + + > header { + display: flex; + position: relative; + z-index: 2; + // TODO + // position: sticky; + // top: var(--stickyTopOffset); + // backdrop-filter: blur(20px); + + > .title { + margin: 0; + padding: 12px 16px 12px 8px; + + > [data-icon] { + margin-right: 6px; + } + + &:empty { + display: none; + } + } + + > .divider { + flex: 1; + margin: auto; + height: 1px; + background: var(--divider); + } + + > button { + width: 42px; + } + } + + &.max-width_500px { + > header { + > .title { + padding: 8px 10px; + } + } + } +} +</style> diff --git a/src/client/components/url-preview.vue b/src/client/components/url-preview.vue index 78afe6a65c..a5052d6132 100644 --- a/src/client/components/url-preview.vue +++ b/src/client/components/url-preview.vue @@ -6,7 +6,7 @@ <div v-else-if="tweetId && tweetExpanded" class="twitter" ref="twitter"> <iframe ref="tweet" scrolling="no" frameborder="no" :style="{ position: 'relative', left: `${tweetLeft}px`, width: `${tweetLeft < 0 ? 'auto' : '100%'}`, height: `${tweetHeight}px` }" :src="`https://platform.twitter.com/embed/index.html?embedId=${embedId}&hideCard=false&hideThread=false&lang=en&theme=${$store.state.device.darkMode ? 'dark' : 'light'}&id=${tweetId}`"></iframe> </div> -<div v-else class="mk-url-preview" v-size="[{ max: 400 }, { max: 350 }]"> +<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"> <div class="thumbnail" v-if="thumbnail" :style="`background-image: url('${thumbnail}')`"> |