Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MS] Enhance sidebar behaviour #8851

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions client/src/services/sidebarMenu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,29 @@
import { ref } from 'vue';

const defaultWidth = 300;
const initialWidth = ref<number>(defaultWidth);
const hiddenWidth = 2;
const computedWidth = ref<number>(defaultWidth);
const storedWidth = ref<number>(defaultWidth);

export default function useSidebarMenu(): any {
function isVisible(): boolean {
return computedWidth.value > 4;
}

function reset(): void {
initialWidth.value = defaultWidth;
computedWidth.value = defaultWidth;
computedWidth.value = storedWidth.value;
}

function hide(): void {
storedWidth.value = computedWidth.value;
computedWidth.value = hiddenWidth;
}

return {
defaultWidth,
initialWidth,
computedWidth,
storedWidth,
isVisible,
reset,
hide,
};
}
20 changes: 2 additions & 18 deletions client/src/views/client-area/ClientAreaHeader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,6 @@

<template>
<div class="header-content">
<ion-button
v-if="!isMobile() && !isSidebarMenuVisible()"
slot="icon-only"
id="trigger-toggle-menu-button"
class="ion-hide-lg-down topbar-button__item"
@click="resetSidebarMenu()"
>
<ion-icon
slot="icon-only"
:icon="menu"
/>
</ion-button>
<ion-title class="header-title title-h1">{{ $msTranslate(title) }}</ion-title>

<div class="header-right">
Expand Down Expand Up @@ -45,18 +33,14 @@

<script setup lang="ts">
import { BmsAccessInstance, PersonalInformationResultData } from '@/services/bms';
import { IonText, IonTitle, IonIcon, IonButton } from '@ionic/vue';
import { IonText, IonTitle, IonIcon } from '@ionic/vue';
import { Translatable } from 'megashark-lib';
import { onMounted, ref } from 'vue';
import { cog, menu } from 'ionicons/icons';
import { cog } from 'ionicons/icons';
import UserAvatarName from '@/components/users/UserAvatarName.vue';
import { isMobile } from '@/parsec';
import useSidebarMenu from '@/services/sidebarMenu';
import { ClientAreaPages } from '@/views/client-area/types';
import { openSettingsModal } from '@/views/settings';

const { isVisible: isSidebarMenuVisible, reset: resetSidebarMenu } = useSidebarMenu();

defineProps<{
title: Translatable;
}>();
Expand Down
23 changes: 10 additions & 13 deletions client/src/views/client-area/ClientAreaPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<div
class="resize-divider"
ref="divider"
v-show="isVisible()"
/>
<ion-split-pane
when="xs"
Expand Down Expand Up @@ -140,12 +141,12 @@ import CustomOrderProcessingPage from '@/views/client-area/dashboard/CustomOrder

const injectionProvider: InjectionProvider = inject(InjectionProviderKey)!;
const informationManager: InformationManager = injectionProvider.getDefault().informationManager;
const { defaultWidth, initialWidth, computedWidth } = useSidebarMenu();
const { computedWidth: computedWidth, isVisible: isVisible } = useSidebarMenu();
const organizations = ref<Array<BmsOrganization>>([]);
const divider = ref();
const currentPage = ref<ClientAreaPages>(ClientAreaPages.Dashboard);
const currentOrganization = ref<BmsOrganization>(DefaultBmsOrganization);
const sidebarWidthProperty = ref(`${defaultWidth}px`);
const sidebarWidthProperty = ref('');
const loggedIn = ref(false);
const refresh = ref(0);
const querying = ref(true);
Expand All @@ -161,6 +162,7 @@ function setToastOffset(width: number): void {
}

onMounted(async () => {
sidebarWidthProperty.value = `${computedWidth.value}px`;
querying.value = true;
if (!BmsAccessInstance.get().isLoggedIn()) {
loggedIn.value = await BmsAccessInstance.get().tryAutoLogin();
Expand Down Expand Up @@ -213,7 +215,6 @@ onMounted(async () => {
const gesture = createGesture({
gestureName: 'resize-menu',
el: divider.value,
onEnd,
onMove,
});
gesture.enable();
Expand All @@ -238,20 +239,16 @@ async function switchPage(page: ClientAreaPages): Promise<void> {

function onMove(detail: GestureDetail): void {
requestAnimationFrame(() => {
let currentWidth = initialWidth.value + detail.deltaX;
if (currentWidth >= 2 && currentWidth <= 500) {
if (currentWidth <= 150) {
currentWidth = 2;
}
computedWidth.value = currentWidth;
if (detail.currentX < 250) {
computedWidth.value = 250;
} else if (detail.currentX > 370) {
computedWidth.value = 370;
} else {
computedWidth.value = detail.currentX;
}
});
}

function onEnd(): void {
initialWidth.value = computedWidth.value;
}

async function onOrganizationSelected(organization: BmsOrganization): Promise<void> {
await navigateTo(Routes.ClientArea, {
skipHandle: true,
Expand Down
13 changes: 8 additions & 5 deletions client/src/views/header/HeaderPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
<!-- icon visible when menu is hidden -->
<ion-buttons slot="start">
<ion-button
v-if="!isMobile() && !isSidebarMenuVisible()"
v-if="!isMobile()"
slot="icon-only"
id="trigger-toggle-menu-button"
class="ion-hide-lg-down topbar-button__item"
@click="resetSidebarMenu()"
class="topbar-button__item"
@click="isSidebarMenuVisible() ? hideSidebarMenu() : resetSidebarMenu()"
>
<ion-icon
slot="icon-only"
Expand Down Expand Up @@ -52,7 +52,10 @@
</div>

<!-- icon menu on mobile -->
<ion-buttons slot="start">
<ion-buttons
slot="start"
v-if="isMobile()"
>
<ion-menu-button />
</ion-buttons>
<!-- end of icon menu on mobile -->
Expand Down Expand Up @@ -159,7 +162,7 @@ import { Ref, inject, onMounted, onUnmounted, ref } from 'vue';
const hotkeyManager: HotkeyManager = inject(HotkeyManagerKey)!;
let hotkeys: HotkeyGroup | null = null;
const workspaceName = ref('');
const { isVisible: isSidebarMenuVisible, reset: resetSidebarMenu } = useSidebarMenu();
const { isVisible: isSidebarMenuVisible, reset: resetSidebarMenu, hide: hideSidebarMenu } = useSidebarMenu();
const userInfo: Ref<ClientInfo | null> = ref(null);
const fullPath: Ref<RouterPathNode[]> = ref([]);
const notificationPopoverIsVisible: Ref<boolean> = ref(false);
Expand Down
82 changes: 58 additions & 24 deletions client/src/views/sidebar-menu/SidebarMenuPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@
<div
class="resize-divider"
ref="divider"
v-show="isVisible()"
/>
<ion-split-pane content-id="main">
<ion-split-pane
content-id="main"
:when="true"
>
<ion-menu
content-id="main"
class="sidebar"
Expand Down Expand Up @@ -50,10 +54,10 @@
button
>
<ion-icon
class="button-icon"
class="organization-card-buttons__icon"
:icon="userInfo && userInfo.currentProfile === UserProfile.Admin ? cog : informationCircle"
/>
<span>
<span class="organization-card-buttons__text">
{{
userInfo && userInfo.currentProfile === UserProfile.Admin
? $msTranslate('SideMenu.manageOrganization')
Expand All @@ -69,10 +73,10 @@
@click="navigateTo(Routes.Workspaces)"
>
<ion-icon
class="button-icon"
class="organization-card-buttons__icon"
:icon="home"
/>
<span>
<span class="organization-card-buttons__text">
{{ $msTranslate('SideMenu.goToHome') }}
</span>
</ion-text>
Expand Down Expand Up @@ -268,7 +272,7 @@
v-show="currentRouteIsOrganizationManagementRoute()"
class="manage-organization"
>
<ion-label class="title-h4">
<ion-label class="title-h4 manage-organization-title">
{{
userInfo && userInfo.currentProfile === UserProfile.Admin
? $msTranslate('SideMenu.manageOrganization')
Expand Down Expand Up @@ -326,7 +330,7 @@
class="sidebar-item-icon"
slot="start"
/>
<ion-label>{{ $msTranslate('SideMenu.organizationInfo') }}</ion-label>
<ion-label class="sidebar-item-text">{{ $msTranslate('SideMenu.organizationInfo') }}</ion-label>
</ion-item>
</ion-list>
</div>
Expand Down Expand Up @@ -412,23 +416,27 @@ import { Ref, computed, inject, onMounted, onUnmounted, ref, watch } from 'vue';
import { Duration } from 'luxon';
import { recentFileManager, RecentFile } from '@/services/recentFiles';
import { openPath } from '@/services/fileOpener';
import { SIDEBAR_MENU_DATA_KEY, SidebarDefaultData, SidebarSavedData } from '@/views/sidebar-menu/utils';

const workspaces: Ref<Array<WorkspaceInfo>> = ref([]);
const eventDistributor: EventDistributor = inject(EventDistributorKey)!;
const informationManager: InformationManager = inject(InformationManagerKey)!;
const storageManager: StorageManager = inject(StorageManagerKey)!;
let eventDistributorCbId: string | null = null;
const divider = ref();
const { defaultWidth, initialWidth, computedWidth } = useSidebarMenu();
const { computedWidth: computedWidth, storedWidth: storedWidth, isVisible: isVisible } = useSidebarMenu();
const userInfo: Ref<ClientInfo | null> = ref(null);
const currentDevice = ref<AvailableDevice | null>(null);
const favorites: Ref<WorkspaceID[]> = ref([]);
const sidebarWidthProperty = ref(`${defaultWidth}px`);
const sidebarWidthProperty = ref('');
const isTrialOrg = ref(false);
const expirationDuration = ref<Duration | undefined>(undefined);
const isExpired = ref(false);
const loggedInDevices = ref<LoggedInDeviceInfo[]>([]);

const MIN_WIDTH = 150;
const MAX_WIDTH = 370;

const watchSidebarWidthCancel = watch(computedWidth, (value: number) => {
sidebarWidthProperty.value = `${value}px`;
// set toast offset
Expand Down Expand Up @@ -495,6 +503,15 @@ onMounted(async () => {
},
);

const savedSidebarData = await storageManager.retrieveComponentData<SidebarSavedData>(SIDEBAR_MENU_DATA_KEY, SidebarDefaultData);
if (savedSidebarData.hidden) {
computedWidth.value = 2;
storedWidth.value = savedSidebarData.width;
} else {
computedWidth.value = savedSidebarData.width;
}
sidebarWidthProperty.value = `${computedWidth.value}px`;

const connInfo = getConnectionInfo();
if (connInfo) {
isExpired.value = connInfo.isExpired;
Expand All @@ -506,7 +523,6 @@ onMounted(async () => {
const gesture = createGesture({
gestureName: 'resize-menu',
el: divider.value,
onEnd,
onMove,
});
gesture.enable();
Expand All @@ -520,10 +536,14 @@ onMounted(async () => {
}
});

onUnmounted(() => {
onUnmounted(async () => {
if (eventDistributorCbId) {
eventDistributor.removeCallback(eventDistributorCbId);
}
await storageManager.storeComponentData<SidebarSavedData>(SIDEBAR_MENU_DATA_KEY, {
width: computedWidth.value < MIN_WIDTH ? storedWidth.value : computedWidth.value,
hidden: computedWidth.value < MIN_WIDTH,
});
watchSidebarWidthCancel();
setToastOffset(0);
});
Expand All @@ -548,19 +568,13 @@ const nonFavoriteWorkspaces = computed(() => {
});

function onMove(detail: GestureDetail): void {
requestAnimationFrame(() => {
let currentWidth = initialWidth.value + detail.deltaX;
if (currentWidth >= 2 && currentWidth <= 500) {
if (currentWidth <= 150) {
currentWidth = 2;
}
computedWidth.value = currentWidth;
}
});
}

function onEnd(): void {
initialWidth.value = computedWidth.value;
if (detail.currentX < MIN_WIDTH) {
computedWidth.value = MIN_WIDTH;
} else if (detail.currentX > MAX_WIDTH) {
computedWidth.value = MAX_WIDTH;
} else {
computedWidth.value = detail.currentX;
}
}

async function openOrganizationChoice(event: Event): Promise<void> {
Expand Down Expand Up @@ -773,6 +787,17 @@ async function removeRecentFile(file: RecentFile): Promise<void> {
cursor: default;
}
}

&__icon {
position: absolute;
}

&__text {
margin-left: 1.4rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
}

Expand Down Expand Up @@ -914,6 +939,12 @@ ion-menu {
margin-inline-end: 12px;
}

&-text {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

& > ion-label {
--color: var(--parsec-color-light-primary-100);
}
Expand Down Expand Up @@ -1032,6 +1063,9 @@ ion-menu {
.title-h4 {
padding: 1.5em 0 1em;
border-top: 1px solid var(--parsec-color-light-primary-30-opacity15);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

&-list {
Expand Down
5 changes: 5 additions & 0 deletions client/src/views/sidebar-menu/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Parsec Cloud (https://parsec.cloud) Copyright (c) BUSL-1.1 2016-present Scille SAS

import { SidebarSavedData } from '@/views/sidebar-menu/utils';

export { SidebarSavedData };
10 changes: 10 additions & 0 deletions client/src/views/sidebar-menu/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Parsec Cloud (https://parsec.cloud) Copyright (c) BUSL-1.1 2016-present Scille SAS

export interface SidebarSavedData {
width?: number;
hidden?: boolean;
}

export const SIDEBAR_MENU_DATA_KEY = 'SidebarMenu';

export const SidebarDefaultData: Required<SidebarSavedData> = { width: 300, hidden: false };
1 change: 1 addition & 0 deletions newsfragments/8851.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed a few visual and functional bugs with the sidebar