feat: add resizable sidebar with splitter layout

This commit is contained in:
pa
2025-09-23 01:19:31 +09:00
committed by Natsumi
parent b75c1b1425
commit 24ea1887b7
5 changed files with 55 additions and 55 deletions
+28 -20
View File
@@ -13,39 +13,42 @@
<VRCXUpdateDialog></VRCXUpdateDialog> <VRCXUpdateDialog></VRCXUpdateDialog>
<template v-if="watchState.isLoggedIn"> <template v-if="watchState.isLoggedIn">
<!-- ### Menu ### -->
<NavMenu></NavMenu> <NavMenu></NavMenu>
<!-- ### Sidebar ### --> <el-splitter @resize-end="setAsideWidth" v-show="isSideBarTabShow">
<Sidebar></Sidebar> <el-splitter-panel>
<Feed></Feed>
<!-- ### Tabs ### --> <GameLog></GameLog>
<Feed></Feed>
<GameLog></GameLog> <PlayerList></PlayerList>
<PlayerList></PlayerList> <Search></Search>
<Search></Search> <Favorites></Favorites>
<Favorites></Favorites> <FriendLog></FriendLog>
<FriendLog></FriendLog> <Moderation></Moderation>
<Moderation></Moderation> <Notification></Notification>
<Notification></Notification> <Tools></Tools>
<Profile></Profile>
<Settings></Settings>
</el-splitter-panel>
<el-splitter-panel :min="200" :max="700" :size="asideWidth">
<Sidebar></Sidebar>
</el-splitter-panel>
</el-splitter>
<FriendList></FriendList> <FriendList></FriendList>
<Charts></Charts> <Charts></Charts>
<Tools></Tools>
<Profile></Profile>
<Settings></Settings>
<!-- ## Dialogs ## --> <!-- ## Dialogs ## -->
<UserDialog></UserDialog> <UserDialog></UserDialog>
@@ -137,9 +140,10 @@
import PrimaryPasswordDialog from './views/Settings/dialogs/PrimaryPasswordDialog.vue'; import PrimaryPasswordDialog from './views/Settings/dialogs/PrimaryPasswordDialog.vue';
import { onMounted, computed, onBeforeMount } from 'vue'; import { onMounted, computed, onBeforeMount } from 'vue';
import { createGlobalStores } from './stores';
import { watchState } from './service/watchState';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { storeToRefs } from 'pinia';
import { createGlobalStores, useAppearanceSettingsStore } from './stores';
import { watchState } from './service/watchState';
import { initNoty } from './plugin/noty'; import { initNoty } from './plugin/noty';
console.log(`isLinux: ${LINUX}`); console.log(`isLinux: ${LINUX}`);
@@ -186,4 +190,8 @@
store.vrcx.checkAutoBackupRestoreVrcRegistry(); store.vrcx.checkAutoBackupRestoreVrcRegistry();
store.game.checkVRChatDebugLogging(); store.game.checkVRChatDebugLogging();
}); });
const appearanceStore = useAppearanceSettingsStore();
const { setAsideWidth } = appearanceStore;
const { asideWidth, isSideBarTabShow } = storeToRefs(appearanceStore);
</script> </script>
+4
View File
@@ -241,6 +241,8 @@ a {
padding: 10px; padding: 10px;
overflow: hidden auto; overflow: hidden auto;
background: #fff; background: #fff;
box-sizing: border-box;
height: 100%;
} }
.x-login-container { .x-login-container {
@@ -366,6 +368,8 @@ hr.x-vertical-divider {
background: #f8f8f8; background: #f8f8f8;
padding: 5px; padding: 5px;
order: 99; order: 99;
height: 100%;
box-sizing: border-box;
} }
// .el-popper.x-quick-search { // .el-popper.x-quick-search {
+21 -6
View File
@@ -23,6 +23,7 @@ import { useNotificationStore } from '../notification';
import { useUserStore } from '../user'; import { useUserStore } from '../user';
import { useVrStore } from '../vr'; import { useVrStore } from '../vr';
import { useVrcxStore } from '../vrcx'; import { useVrcxStore } from '../vrcx';
import { useUiStore } from '../ui';
export const useAppearanceSettingsStore = defineStore( export const useAppearanceSettingsStore = defineStore(
'AppearanceSettings', 'AppearanceSettings',
@@ -36,6 +37,7 @@ export const useAppearanceSettingsStore = defineStore(
const gameLogStore = useGameLogStore(); const gameLogStore = useGameLogStore();
const vrcxStore = useVrcxStore(); const vrcxStore = useVrcxStore();
const userStore = useUserStore(); const userStore = useUserStore();
const uiStore = useUiStore();
const { t, availableLocales, locale } = useI18n(); const { t, availableLocales, locale } = useI18n();
@@ -250,6 +252,12 @@ export const useAppearanceSettingsStore = defineStore(
const randomUserColours = computed(() => state.randomUserColours); const randomUserColours = computed(() => state.randomUserColours);
const trustColor = computed(() => state.trustColor); const trustColor = computed(() => state.trustColor);
const currentCulture = computed(() => state.currentCulture); const currentCulture = computed(() => state.currentCulture);
const isSideBarTabShow = computed(() => {
return !(
uiStore.menuActiveIndex === 'friendList' ||
uiStore.menuActiveIndex === 'charts'
);
});
watch( watch(
() => watchState.isFriendsLoaded, () => watchState.isFriendsLoaded,
@@ -521,13 +529,19 @@ export const useAppearanceSettingsStore = defineStore(
); );
} }
/** /**
* @param {number} width * @param {number} panelNumber
* @param {Array<number>} widthArray
*/ */
function setAsideWidth(width) { function setAsideWidth(panelNumber, widthArray) {
requestAnimationFrame(() => { if (Array.isArray(widthArray) && widthArray[1]) {
state.asideWidth = width; requestAnimationFrame(() => {
configRepository.setInt('VRCX_sidePanelWidth', width); state.asideWidth = widthArray[1];
}); configRepository.setInt(
'VRCX_sidePanelWidth',
widthArray[1]
);
});
}
} }
function setIsSidebarGroupByInstance() { function setIsSidebarGroupByInstance() {
state.isSidebarGroupByInstance = !state.isSidebarGroupByInstance; state.isSidebarGroupByInstance = !state.isSidebarGroupByInstance;
@@ -735,6 +749,7 @@ export const useAppearanceSettingsStore = defineStore(
randomUserColours, randomUserColours,
trustColor, trustColor,
currentCulture, currentCulture,
isSideBarTabShow,
setAppLanguage, setAppLanguage,
setDisplayVRCPlusIconsAsAvatar, setDisplayVRCPlusIconsAsAvatar,
-14
View File
@@ -586,19 +586,6 @@
</el-option-group> </el-option-group>
</el-select> </el-select>
</div> </div>
<div class="options-container-item">
<span class="name" style="vertical-align: top; padding-top: 10px">{{
t('view.settings.appearance.side_panel.width')
}}</span>
<el-slider
:model-value="asideWidth"
:show-tooltip="false"
:marks="{ 300: '' }"
:min="200"
:max="500"
style="display: inline-block; width: 300px; padding-top: 16px"
@input="setAsideWidth"></el-slider>
</div>
<simple-switch <simple-switch
:label="t('view.settings.appearance.side_panel.group_by_instance')" :label="t('view.settings.appearance.side_panel.group_by_instance')"
:value="isSidebarGroupByInstance" :value="isSidebarGroupByInstance"
@@ -2075,7 +2062,6 @@
setSidebarSortMethod1, setSidebarSortMethod1,
setSidebarSortMethod2, setSidebarSortMethod2,
setSidebarSortMethod3, setSidebarSortMethod3,
setAsideWidth,
setIsSidebarGroupByInstance, setIsSidebarGroupByInstance,
setIsHideFriendsInSameInstance, setIsHideFriendsInSameInstance,
setIsSidebarDivideByFriendGroup, setIsSidebarDivideByFriendGroup,
+2 -15
View File
@@ -1,5 +1,5 @@
<template> <template>
<div v-show="isSideBarTabShow" id="aside" class="x-aside-container" :style="{ width: `${asideWidth}px` }"> <div class="x-aside-container">
<div style="display: flex; align-items: baseline"> <div style="display: flex; align-items: baseline">
<el-select <el-select
clearable clearable
@@ -81,31 +81,18 @@
<script setup> <script setup>
import { Refresh, Compass } from '@element-plus/icons-vue'; import { Refresh, Compass } from '@element-plus/icons-vue';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { computed } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { userImage } from '../../shared/utils'; import { userImage } from '../../shared/utils';
import { import { useFriendStore, useGroupStore, useSearchStore } from '../../stores';
useAppearanceSettingsStore,
useFriendStore,
useGroupStore,
useSearchStore,
useUiStore
} from '../../stores';
import FriendsSidebar from './components/FriendsSidebar.vue'; import FriendsSidebar from './components/FriendsSidebar.vue';
import GroupsSidebar from './components/GroupsSidebar.vue'; import GroupsSidebar from './components/GroupsSidebar.vue';
const { friends, isRefreshFriendsLoading, onlineFriendCount } = storeToRefs(useFriendStore()); const { friends, isRefreshFriendsLoading, onlineFriendCount } = storeToRefs(useFriendStore());
const { refreshFriendsList, confirmDeleteFriend } = useFriendStore(); const { refreshFriendsList, confirmDeleteFriend } = useFriendStore();
const { asideWidth } = storeToRefs(useAppearanceSettingsStore());
const { menuActiveIndex } = storeToRefs(useUiStore());
const { quickSearchRemoteMethod, quickSearchChange, directAccessPaste } = useSearchStore(); const { quickSearchRemoteMethod, quickSearchChange, directAccessPaste } = useSearchStore();
const { quickSearchItems } = storeToRefs(useSearchStore()); const { quickSearchItems } = storeToRefs(useSearchStore());
const { inGameGroupOrder, groupInstances } = storeToRefs(useGroupStore()); const { inGameGroupOrder, groupInstances } = storeToRefs(useGroupStore());
const { t } = useI18n(); const { t } = useI18n();
const isSideBarTabShow = computed(() => {
return !(menuActiveIndex.value === 'friendList' || menuActiveIndex.value === 'charts');
});
</script> </script>
<style scoped> <style scoped>