Fold sidebar (#6610)

* wip

* wip
This commit is contained in:
syuilo 2020-08-01 10:53:23 +09:00 committed by GitHub
parent 8e905b3a7d
commit a193114347
6 changed files with 138 additions and 88 deletions

View File

@ -556,6 +556,12 @@ testEmail: "配信テスト"
wordMute: "ワードミュート" wordMute: "ワードミュート"
userSaysSomething: "{name}が何かを言いました" userSaysSomething: "{name}が何かを言いました"
makeActive: "アクティブにする" makeActive: "アクティブにする"
display: "表示"
_sidebar:
full: "フル"
icon: "アイコン"
hide: "隠す"
_wordMute: _wordMute:
muteWords: "ミュートするワード" muteWords: "ミュートするワード"

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="mk-app" v-hotkey.global="keymap"> <div class="mk-app" v-hotkey.global="keymap">
<header class="header"> <header class="header" ref="header">
<div class="title" ref="title"> <div class="title" ref="title">
<transition :name="$store.state.device.animation ? 'header' : ''" mode="out-in" appear> <transition :name="$store.state.device.animation ? 'header' : ''" mode="out-in" appear>
<button class="_button back" v-if="canBack" @click="back()"><fa :icon="faChevronLeft"/></button> <button class="_button back" v-if="canBack" @click="back()"><fa :icon="faChevronLeft"/></button>
@ -31,7 +31,7 @@
</div> </div>
</header> </header>
<x-sidebar ref="nav"/> <x-sidebar ref="nav" @change-view-mode="calcHeaderWidth"/>
<div class="contents" ref="contents" :class="{ wallpaper }"> <div class="contents" ref="contents" :class="{ wallpaper }">
<main ref="main"> <main ref="main">
@ -77,7 +77,7 @@
</template> </template>
</div> </div>
<div class="buttons"> <div class="buttons" :class="{ navHidden }">
<button class="button nav _button" @click="showNav" ref="navButton"><fa :icon="faBars"/><i v-if="navIndicated"><fa :icon="faCircle"/></i></button> <button class="button nav _button" @click="showNav" ref="navButton"><fa :icon="faBars"/><i v-if="navIndicated"><fa :icon="faCircle"/></i></button>
<button v-if="$route.name === 'index'" class="button home _button" @click="top()"><fa :icon="faHome"/></button> <button v-if="$route.name === 'index'" class="button home _button" @click="top()"><fa :icon="faHome"/></button>
<button v-else class="button home _button" @click="$router.push('/')"><fa :icon="faHome"/></button> <button v-else class="button home _button" @click="$router.push('/')"><fa :icon="faHome"/></button>
@ -85,7 +85,7 @@
<button v-if="$store.getters.isSignedIn" class="button post _buttonPrimary" @click="post()"><fa :icon="faPencilAlt"/></button> <button v-if="$store.getters.isSignedIn" class="button post _buttonPrimary" @click="post()"><fa :icon="faPencilAlt"/></button>
</div> </div>
<button v-if="$store.getters.isSignedIn" class="post _buttonPrimary" @click="post()"><fa :icon="faPencilAlt"/></button> <button v-if="$store.getters.isSignedIn" class="post _buttonPrimary" :class="{ navHidden }" @click="post()"><fa :icon="faPencilAlt"/></button>
<stream-indicator v-if="$store.getters.isSignedIn"/> <stream-indicator v-if="$store.getters.isSignedIn"/>
</div> </div>
@ -124,6 +124,7 @@ export default Vue.extend({
isDesktop: window.innerWidth >= DESKTOP_THRESHOLD, isDesktop: window.innerWidth >= DESKTOP_THRESHOLD,
canBack: false, canBack: false,
menuDef: this.$store.getters.nav({}), menuDef: this.$store.getters.nav({}),
navHidden: false,
wallpaper: localStorage.getItem('wallpaper') != null, wallpaper: localStorage.getItem('wallpaper') != null,
faGripVertical, faChevronLeft, faComments, faHashtag, faBroadcastTower, faFireAlt, faEllipsisH, faPencilAlt, faBars, faTimes, faBell, faSearch, faUserCog, faCog, faUser, faHome, faStar, faCircle, faAt, faEnvelope, faListUl, faPlus, faUserClock, faLaugh, faUsers, faTachometerAlt, faExchangeAlt, faGlobe, faChartBar, faCloud, faServer, faProjectDiagram faGripVertical, faChevronLeft, faComments, faHashtag, faBroadcastTower, faFireAlt, faEllipsisH, faPencilAlt, faBars, faTimes, faBell, faSearch, faUserCog, faCog, faUser, faHome, faStar, faCircle, faAt, faEnvelope, faListUl, faPlus, faUserClock, faLaugh, faUsers, faTachometerAlt, faExchangeAlt, faGlobe, faChartBar, faCloud, faServer, faProjectDiagram
}; };
@ -225,22 +226,15 @@ export default Vue.extend({
}, },
mounted() { mounted() {
const adjustTitlePosition = () => { this.adjustTitlePosition();
const left = this.$refs.main.getBoundingClientRect().left - this.$refs.nav.$el.offsetWidth;
if (left >= 0) {
this.$refs.title.style.left = left + 'px';
}
};
adjustTitlePosition();
const ro = new ResizeObserver((entries, observer) => { const ro = new ResizeObserver((entries, observer) => {
adjustTitlePosition(); this.adjustTitlePosition();
}); });
ro.observe(this.$refs.contents); ro.observe(this.$refs.contents);
window.addEventListener('resize', adjustTitlePosition, { passive: true }); window.addEventListener('resize', this.adjustTitlePosition, { passive: true });
if (!this.isDesktop) { if (!this.isDesktop) {
window.addEventListener('resize', () => { window.addEventListener('resize', () => {
@ -250,9 +244,27 @@ export default Vue.extend({
// widget follow // widget follow
this.attachSticky(); this.attachSticky();
this.$nextTick(() => {
this.calcHeaderWidth();
});
}, },
methods: { methods: {
adjustTitlePosition() {
const left = this.$refs.main.getBoundingClientRect().left - this.$refs.nav.$el.offsetWidth;
if (left >= 0) {
this.$refs.title.style.left = left + 'px';
}
},
calcHeaderWidth() {
const navWidth = this.$refs.nav.$el.offsetWidth;
this.navHidden = navWidth === 0;
this.$refs.header.style.width = `calc(100% - ${navWidth}px)`;
this.adjustTitlePosition();
},
showNav() { showNav() {
this.$refs.nav.show(); this.$refs.nav.show();
}, },
@ -373,12 +385,8 @@ export default Vue.extend({
<style lang="scss" scoped> <style lang="scss" scoped>
.mk-app { .mk-app {
$header-height: 60px; $header-height: 60px;
$nav-width: 250px; // TODO:
$nav-icon-only-width: 80px; // TODO:
$main-width: 670px; $main-width: 670px;
$ui-font-size: 1em; // TODO: $ui-font-size: 1em; // TODO:
$nav-icon-only-threshold: 1279px; // TODO:
$nav-hide-threshold: 650px; // TODO:
$header-sub-hide-threshold: 1090px; $header-sub-hide-threshold: 1090px;
$left-widgets-hide-threshold: 1600px; $left-widgets-hide-threshold: 1600px;
$right-widgets-hide-threshold: 1090px; $right-widgets-hide-threshold: 1090px;
@ -399,21 +407,13 @@ export default Vue.extend({
top: 0; top: 0;
right: 0; right: 0;
height: $header-height; height: $header-height;
width: calc(100% - #{$nav-width}); width: 100%;
//background-color: var(--panel); //background-color: var(--panel);
-webkit-backdrop-filter: blur(32px); -webkit-backdrop-filter: blur(32px);
backdrop-filter: blur(32px); backdrop-filter: blur(32px);
background-color: var(--header); background-color: var(--header);
border-bottom: solid 1px var(--divider); border-bottom: solid 1px var(--divider);
@media (max-width: $nav-icon-only-threshold) {
width: calc(100% - #{$nav-icon-only-width});
}
@media (max-width: $nav-hide-threshold) {
width: 100%;
}
> .title { > .title {
position: relative; position: relative;
line-height: $header-height; line-height: $header-height;
@ -683,7 +683,7 @@ export default Vue.extend({
} }
> .post { > .post {
display: none; display: block;
position: fixed; position: fixed;
z-index: 1000; z-index: 1000;
bottom: 32px; bottom: 32px;
@ -694,8 +694,8 @@ export default Vue.extend({
box-shadow: 0 3px 5px -1px rgba(0, 0, 0, 0.2), 0 6px 10px 0 rgba(0, 0, 0, 0.14), 0 1px 18px 0 rgba(0, 0, 0, 0.12); box-shadow: 0 3px 5px -1px rgba(0, 0, 0, 0.2), 0 6px 10px 0 rgba(0, 0, 0, 0.14), 0 1px 18px 0 rgba(0, 0, 0, 0.12);
font-size: 22px; font-size: 22px;
@media (min-width: ($nav-hide-threshold + 1px)) { &.navHidden {
display: block; display: none;
} }
@media (min-width: ($header-sub-hide-threshold + 1px)) { @media (min-width: ($header-sub-hide-threshold + 1px)) {
@ -717,7 +717,7 @@ export default Vue.extend({
padding: 0 16px 16px 16px; padding: 0 16px 16px 16px;
} }
@media (min-width: ($nav-hide-threshold + 1px)) { &:not(.navHidden) {
display: none; display: none;
} }

View File

@ -9,7 +9,7 @@
</transition> </transition>
<transition name="nav"> <transition name="nav">
<nav class="nav" v-show="showing"> <nav class="nav" :class="{ iconOnly, hidden }" v-show="showing">
<div> <div>
<button class="item _button account" @click="openAccountMenu" v-if="$store.getters.isSignedIn"> <button class="item _button account" @click="openAccountMenu" v-if="$store.getters.isSignedIn">
<mk-avatar :user="$store.state.i" class="avatar"/><mk-acct class="text" :user="$store.state.i"/> <mk-avatar :user="$store.state.i" class="avatar"/><mk-acct class="text" :user="$store.state.i"/>
@ -62,6 +62,8 @@ export default Vue.extend({
menuDef: this.$store.getters.nav({ menuDef: this.$store.getters.nav({
search: this.search search: this.search
}), }),
iconOnly: false,
hidden: false,
faGripVertical, faChevronLeft, faComments, faHashtag, faBroadcastTower, faFireAlt, faEllipsisH, faPencilAlt, faBars, faTimes, faBell, faSearch, faUserCog, faCog, faUser, faHome, faStar, faCircle, faAt, faEnvelope, faListUl, faPlus, faUserClock, faLaugh, faUsers, faTachometerAlt, faExchangeAlt, faGlobe, faChartBar, faCloud, faServer, faProjectDiagram faGripVertical, faChevronLeft, faComments, faHashtag, faBroadcastTower, faFireAlt, faEllipsisH, faPencilAlt, faBars, faTimes, faBell, faSearch, faUserCog, faCog, faUser, faHome, faStar, faCircle, faAt, faEnvelope, faListUl, faPlus, faUserClock, faLaugh, faUsers, faTachometerAlt, faExchangeAlt, faGlobe, faChartBar, faCloud, faServer, faProjectDiagram
}; };
}, },
@ -85,9 +87,35 @@ export default Vue.extend({
$route(to, from) { $route(to, from) {
this.showing = false; this.showing = false;
}, },
'$store.state.device.sidebarDisplay'() {
this.calcViewState();
},
iconOnly() {
this.$nextTick(() => {
this.$emit('change-view-mode');
});
},
hidden() {
this.$nextTick(() => {
this.$emit('change-view-mode');
});
}
},
created() {
window.addEventListener('resize', this.calcViewState);
this.calcViewState();
}, },
methods: { methods: {
calcViewState() {
this.iconOnly = (window.innerWidth <= 1279) || (this.$store.state.device.sidebarDisplay === 'icon');
this.hidden = (window.innerWidth <= 650) || (this.$store.state.device.sidebarDisplay === 'hide');
},
show() { show() {
this.showing = true; this.showing = true;
}, },
@ -314,10 +342,8 @@ export default Vue.extend({
.mvcprjjd { .mvcprjjd {
$ui-font-size: 1em; // TODO: $ui-font-size: 1em; // TODO:
$nav-width: 250px; // TODO: $nav-width: 250px;
$nav-icon-only-width: 80px; // TODO: $nav-icon-only-width: 80px;
$nav-icon-only-threshold: 1279px; // TODO:
$nav-hide-threshold: 650px; // TODO:
> .nav-back { > .nav-back {
z-index: 1001; z-index: 1001;
@ -331,19 +357,66 @@ export default Vue.extend({
width: $nav-width; width: $nav-width;
box-sizing: border-box; box-sizing: border-box;
@media (max-width: $nav-icon-only-threshold) { &.iconOnly {
flex: 0 0 $nav-icon-only-width; flex: 0 0 $nav-icon-only-width;
width: $nav-icon-only-width; width: $nav-icon-only-width;
&:not(.hidden) {
> div {
width: $nav-icon-only-width;
> .divider {
margin: 8px auto;
width: calc(100% - 32px);
} }
@media (max-width: $nav-hide-threshold) { > .item {
padding-left: 0;
width: 100%;
text-align: center;
font-size: $ui-font-size * 1.2;
line-height: 3.7rem;
> [data-icon],
> .avatar {
margin-right: 0;
}
> i {
left: 10px;
}
> .text {
display: none;
}
&:first-child {
margin-bottom: 8px;
}
&:last-child {
margin-top: 8px;
}
}
}
}
}
&.hidden {
position: fixed; position: fixed;
top: 0; top: 0;
left: 0; left: 0;
z-index: 1001; z-index: 1001;
> div {
> .index,
> .notifications {
display: none;
}
}
} }
@media (min-width: $nav-hide-threshold + 1px) { &:not(.hidden) {
display: block !important; display: block !important;
} }
@ -365,25 +438,6 @@ export default Vue.extend({
border-top: solid 1px var(--divider); border-top: solid 1px var(--divider);
} }
@media (max-width: $nav-icon-only-threshold) and (min-width: $nav-hide-threshold + 1px) {
width: $nav-icon-only-width;
> .divider {
margin: 8px auto;
width: calc(100% - 32px);
}
> .item {
&:first-child {
margin-bottom: 8px;
}
&:last-child {
margin-top: 8px;
}
}
}
> .item { > .item {
position: relative; position: relative;
display: block; display: block;
@ -452,34 +506,6 @@ export default Vue.extend({
margin-top: 16px; margin-top: 16px;
border-top: solid 1px var(--divider); border-top: solid 1px var(--divider);
} }
@media (max-width: $nav-icon-only-threshold) and (min-width: $nav-hide-threshold + 1px) {
padding-left: 0;
width: 100%;
text-align: center;
font-size: $ui-font-size * 1.2;
line-height: 3.7rem;
> [data-icon],
> .avatar {
margin-right: 0;
}
> i {
left: 10px;
}
> .text {
display: none;
}
}
}
@media (max-width: $nav-hide-threshold) {
> .index,
> .notifications {
display: none;
}
} }
} }
} }

View File

@ -42,6 +42,7 @@ export default Vue.extend({
}, },
methods: { methods: {
toggle() { toggle() {
if (this.disabled) return;
this.$emit('change', this.value); this.$emit('change', this.value);
} }
} }
@ -61,7 +62,10 @@ export default Vue.extend({
&.disabled { &.disabled {
opacity: 0.6; opacity: 0.6;
cursor: not-allowed;
&, * {
cursor: not-allowed !important;
}
} }
&.checked { &.checked {

View File

@ -7,6 +7,12 @@
<template #desc><button class="_textButton" @click="addItem">{{ $t('addItem') }}</button></template> <template #desc><button class="_textButton" @click="addItem">{{ $t('addItem') }}</button></template>
</mk-textarea> </mk-textarea>
</div> </div>
<div class="_content">
<div>{{ $t('display') }}</div>
<mk-radio v-model="sidebarDisplay" value="full">{{ $t('_sidebar.full') }}</mk-radio>
<mk-radio v-model="sidebarDisplay" value="icon">{{ $t('_sidebar.icon') }}</mk-radio>
<!-- <mk-radio v-model="sidebarDisplay" value="hide" disabled>{{ $t('_sidebar.hide') }}</mk-radio>--> <!-- TODO: UI -->
</div>
<div class="_footer"> <div class="_footer">
<mk-button inline @click="save()" primary><fa :icon="faSave"/> {{ $t('save') }}</mk-button> <mk-button inline @click="save()" primary><fa :icon="faSave"/> {{ $t('save') }}</mk-button>
<mk-button inline @click="reset()"><fa :icon="faRedo"/> {{ $t('default') }}</mk-button> <mk-button inline @click="reset()"><fa :icon="faRedo"/> {{ $t('default') }}</mk-button>
@ -19,12 +25,14 @@ import Vue from 'vue';
import { faListUl, faSave, faRedo } from '@fortawesome/free-solid-svg-icons'; import { faListUl, faSave, faRedo } from '@fortawesome/free-solid-svg-icons';
import MkButton from '../../components/ui/button.vue'; import MkButton from '../../components/ui/button.vue';
import MkTextarea from '../../components/ui/textarea.vue'; import MkTextarea from '../../components/ui/textarea.vue';
import MkRadio from '../../components/ui/radio.vue';
import { defaultDeviceUserSettings } from '../../store'; import { defaultDeviceUserSettings } from '../../store';
export default Vue.extend({ export default Vue.extend({
components: { components: {
MkButton, MkButton,
MkTextarea, MkTextarea,
MkRadio,
}, },
data() { data() {
@ -38,7 +46,12 @@ export default Vue.extend({
computed: { computed: {
splited(): string[] { splited(): string[] {
return this.items.trim().split('\n').filter(x => x.trim() !== ''); return this.items.trim().split('\n').filter(x => x.trim() !== '');
} },
sidebarDisplay: {
get() { return this.$store.state.device.sidebarDisplay; },
set(value) { this.$store.commit('device/set', { key: 'sidebarDisplay', value }); }
},
}, },
created() { created() {

View File

@ -77,6 +77,7 @@ export const defaultDeviceSettings = {
enableInfiniteScroll: true, enableInfiniteScroll: true,
fixedWidgetsPosition: false, fixedWidgetsPosition: false,
useBlurEffectForModal: true, useBlurEffectForModal: true,
sidebarDisplay: 'full', // full, icon, hide
roomGraphicsQuality: 'medium', roomGraphicsQuality: 'medium',
roomUseOrthographicCamera: true, roomUseOrthographicCamera: true,
deckColumnAlign: 'left', deckColumnAlign: 'left',