44 <div class =" header-breadcrumbs" >
55 <ion-breadcrumbs
66 v-if =" isLargeDisplay"
7- class =" breadcrumb"
7+ class =" breadcrumb-container "
88 @ion-collapsed-click =" openPopover($event)"
99 :max-items =" maxShown"
1010 :items-before-collapse =" itemsBeforeCollapse"
1111 :items-after-collapse =" itemsAfterCollapse"
1212 >
1313 <ion-breadcrumb
1414 v-for =" path in pathNodes"
15- @click =" navigateTo(path)"
15+ @click =" path.display !== currentFolderName ? navigateTo(path) : null"
16+ @contextmenu.prevent =" onRightClick"
1617 :path =" path"
1718 class =" breadcrumb-element breadcrumb-normal"
19+ :class =" {
20+ 'breadcrumb-element--home': props.pathNodes.length === 1,
21+ 'breadcrumb-element--active': path.display === currentFolderName || (props.pathNodes.length === 1 && props.fromHeaderPage),
22+ }"
1823 :key =" path.id"
1924 ref =" breadcrumb"
2025 >
2631 <div class =" breadcrumb-text" >
2732 {{ path.display ? path.display : $msTranslate(path.title) }}
2833 </div >
34+ <ion-icon
35+ v-if =" currentFolderName === path.display"
36+ :icon =" ellipsisHorizontal"
37+ class =" option-icon"
38+ @click =" breadcrumbOptionClick(path, $event)"
39+ />
2940 </ion-breadcrumb >
3041 </ion-breadcrumbs >
3142 <div
@@ -59,12 +70,17 @@ export interface RouterPathNode {
5970
6071<script setup lang="ts">
6172import HeaderBreadcrumbPopover from ' @/components/header/HeaderBreadcrumbPopover.vue' ;
62- import { WorkspaceName } from ' @/parsec' ;
73+ import { openWorkspaceContextMenu } from ' @/components/workspaces' ;
74+ import { StartedWorkspaceInfo , WorkspaceInfo , WorkspaceName , WorkspaceRole } from ' @/parsec' ;
6375import { Query , Routes } from ' @/router' ;
76+ import { EventDistributor , EventDistributorKey , Events , OpenFolderBreadcrumbContextMenuData } from ' @/services/eventDistributor' ;
77+ import { InformationManager } from ' @/services/informationManager' ;
78+ import { StorageManager } from ' @/services/storageManager' ;
79+ import { WorkspaceAttributes } from ' @/services/workspaceAttributes' ;
6480import { IonBreadcrumb , IonBreadcrumbs , IonIcon , IonText , popoverController } from ' @ionic/vue' ;
65- import { chevronDown } from ' ionicons/icons' ;
81+ import { chevronDown , ellipsisHorizontal } from ' ionicons/icons' ;
6682import { Translatable , useWindowSize } from ' megashark-lib' ;
67- import { computed , onMounted , onUnmounted , ref , useTemplateRef , watch } from ' vue' ;
83+ import { computed , inject , onMounted , onUnmounted , ref , useTemplateRef , watch } from ' vue' ;
6884
6985const props = withDefaults (
7086 defineProps <{
@@ -73,21 +89,29 @@ const props = withDefaults(
7389 itemsBeforeCollapse? : number ;
7490 itemsAfterCollapse? : number ;
7591 maxShown? : number ;
76- fromHeaderPage? : boolean ;
92+ fromHeaderPage? : {
93+ workspaceAttributes: WorkspaceAttributes ;
94+ workspace: StartedWorkspaceInfo | WorkspaceInfo ;
95+ ownRole: WorkspaceRole ;
96+ eventDistributor: EventDistributor ;
97+ informationManager: InformationManager ;
98+ storageManager: StorageManager ;
99+ };
77100 availableWidth? : number ;
78101 showParentNode? : boolean ;
79102 }>(),
80103 {
81104 itemsBeforeCollapse: 2 ,
82105 itemsAfterCollapse: 1 ,
83106 maxShown: 3 ,
84- fromHeaderPage: false ,
107+ fromHeaderPage: undefined ,
85108 availableWidth: 0 ,
86109 showParentNode: true ,
87110 },
88111);
89112
90113const { windowWidth, isLargeDisplay, isSmallDisplay } = useWindowSize ();
114+ const eventDistributor: EventDistributor = inject (EventDistributorKey )! ;
91115const breadcrumbRef = useTemplateRef <HTMLIonBreadcrumbElement >(' breadcrumb' );
92116const breadcrumbWidthProperty = ref (' ' );
93117let ignoreNextEvent = false ;
@@ -121,14 +145,37 @@ const currentFolderName = computed(() => {
121145 return ' ' ;
122146 }
123147
124- if (props .pathNodes .length === 1 ) {
125- return props .workspaceName ;
148+ if (props .pathNodes .length === 1 && props . fromHeaderPage ) {
149+ return props .fromHeaderPage . workspace . currentName ;
126150 }
127151
128152 const lastNode = props .pathNodes [props .pathNodes .length - 1 ];
129153 return lastNode .display || props .workspaceName ;
130154});
131155
156+ async function breadcrumbOptionClick(path : RouterPathNode , event : Event ): Promise <void > {
157+ if (! props .fromHeaderPage ) {
158+ return ;
159+ }
160+
161+ if (path .display === props .workspaceName ) {
162+ await openWorkspaceContextMenu (
163+ event ,
164+ props .fromHeaderPage .workspace as WorkspaceInfo ,
165+ props .fromHeaderPage .workspaceAttributes ,
166+ props .fromHeaderPage .eventDistributor ,
167+ props .fromHeaderPage .informationManager ,
168+ props .fromHeaderPage .storageManager ,
169+ true ,
170+ );
171+ } else {
172+ await eventDistributor .dispatchEvent (Events .OpenFolderBreadcrumbContextMenu , {
173+ event ,
174+ role: props .fromHeaderPage .ownRole ,
175+ } as OpenFolderBreadcrumbContextMenuData );
176+ }
177+ }
178+
132179function setBreadcrumbWidth(): void {
133180 if (props .availableWidth > 0 && breadcrumbRef .value ) {
134181 let visibleNodes = Math .min (props .pathNodes .length , props .maxShown );
@@ -192,6 +239,12 @@ function navigateTo(path: RouterPathNode): void {
192239 }
193240 emits (' change' , path );
194241}
242+
243+ async function onRightClick(event : Event ): Promise <void > {
244+ event .preventDefault ();
245+ event .stopPropagation ();
246+ await breadcrumbOptionClick (props .pathNodes [props .pathNodes .length - 1 ], event );
247+ }
195248 </script >
196249
197250<style scoped lang="scss">
@@ -207,13 +260,13 @@ function navigateTo(path: RouterPathNode): void {
207260 }
208261}
209262
210- .breadcrumb {
263+ .breadcrumb-container {
211264 padding : 0 ;
212265 color : var (--parsec-color-light-secondary-grey );
213266 display : flex ;
214267 flex-wrap : nowrap ;
215268
216- & -element {
269+ .breadcrumb -element {
217270 .main-icon {
218271 font-size : 1.125rem ;
219272 }
@@ -240,6 +293,7 @@ function navigateTo(path: RouterPathNode): void {
240293 & :hover:not (.breadcrumb-collapsed ) {
241294 color : var (--parsec-color-light-secondary-text );
242295 position : relative ;
296+ cursor : default ;
243297
244298 & ::after {
245299 content : ' ' ;
@@ -259,28 +313,65 @@ function navigateTo(path: RouterPathNode): void {
259313 }
260314 }
261315 }
262- }
263-
264- // Defined by ionic
265- // eslint-disable-next-line vue-scoped-css/no-unused-selector
266- & -active {
267- color : var (--parsec-color-light-primary-700 );
268- pointer-events : none ;
269316
270- & ::part (native ) {
317+ .breadcrumb-text {
318+ overflow-x : hidden ;
319+ text-overflow : ellipsis ;
320+ white-space : nowrap ;
321+ font-size : 1rem ;
322+ pointer-events : none ;
271323 cursor : default ;
272324 }
273325
274- .main-icon {
326+ & --active :not (.breadcrumb-element--home ) {
327+ color : var (--parsec-color-light-secondary-text );
328+ --background : none ;
329+
330+ & ::part (native ) {
331+ border-radius : var (--parsec-radius-6 );
332+ }
333+
334+ .breadcrumb-text {
335+ pointer-events : none ;
336+ cursor : default ;
337+ }
338+
339+ & :hover:not (.breadcrumb-collapsed ) {
340+ color : var (--parsec-color-light-primary-700 );
341+ position : relative ;
342+
343+ & ::after {
344+ content : none ;
345+ }
346+ }
347+
348+ .option-icon :hover {
349+ background : var (--parsec-color-light-secondary-medium );
350+ border-radius : var (--parsec-radius-8 );
351+ pointer-events : all ;
352+ opacity : 1 ;
353+ cursor : pointer ;
354+ }
355+ }
356+
357+ & --home {
275358 color : var (--parsec-color-light-primary-700 );
276- margin-right : 0.5rem ;
359+
360+ .main-icon {
361+ color : var (--parsec-color-light-primary-700 );
362+ margin-right : 0.5rem ;
363+ }
277364 }
278365 }
279366
280- & -text {
281- overflow-x : hidden ;
282- text-overflow : ellipsis ;
283- white-space : nowrap ;
367+ .option-icon {
368+ font-size : 1.25rem ;
369+ padding : 0.25rem ;
370+ color : var (--parsec-color-light-secondary-text );
371+ margin-left : 0.25rem ;
372+ opacity : 0.3 ;
373+ pointer-events : all ;
374+ flex-shrink : 0 ;
284375 }
285376}
286377
0 commit comments