Merge branch 'main' into 22207-icon-resort
[arvados.git] / services / workbench2 / src / views-components / context-menu / menu-item-sort.ts
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import { ContextMenuAction } from './context-menu-action-set';
6 import { ContextMenuActionNames } from 'views-components/context-menu/context-menu-action-set';
7 import { sortByProperty } from 'common/array-utils';
8 import { horizontalMenuDivider, verticalMenuDivider } from './actions/context-menu-divider';
9 import { MultiSelectMenuAction } from 'views-components/multiselect-toolbar/ms-menu-actions';
10
11 export enum ContextMenuKind {
12     API_CLIENT_AUTHORIZATION = "ApiClientAuthorization",
13     ROOT_PROJECT = "RootProject",
14     ROOT_PROJECT_ADMIN = "RootProjectAdmin",
15     PROJECT = "Project",
16     FILTER_GROUP = "FilterGroup",
17     READONLY_PROJECT = "ReadOnlyProject",
18     FROZEN_PROJECT = "FrozenProject",
19     FROZEN_PROJECT_ADMIN = "FrozenProjectAdmin",
20     PROJECT_ADMIN = "ProjectAdmin",
21     FILTER_GROUP_ADMIN = "FilterGroupAdmin",
22     RESOURCE = "Resource",
23     FAVORITE = "Favorite",
24     TRASH = "Trash",
25     COLLECTION_FILES = "CollectionFiles",
26     COLLECTION_FILES_MULTIPLE = "CollectionFilesMultiple",
27     READONLY_COLLECTION_FILES = "ReadOnlyCollectionFiles",
28     READONLY_COLLECTION_FILES_MULTIPLE = "ReadOnlyCollectionFilesMultiple",
29     COLLECTION_FILES_NOT_SELECTED = "CollectionFilesNotSelected",
30     COLLECTION_FILE_ITEM = "CollectionFileItem",
31     COLLECTION_DIRECTORY_ITEM = "CollectionDirectoryItem",
32     READONLY_COLLECTION_FILE_ITEM = "ReadOnlyCollectionFileItem",
33     READONLY_COLLECTION_DIRECTORY_ITEM = "ReadOnlyCollectionDirectoryItem",
34     COLLECTION = "Collection",
35     COLLECTION_ADMIN = "CollectionAdmin",
36     READONLY_COLLECTION = "ReadOnlyCollection",
37     OLD_VERSION_COLLECTION = "OldVersionCollection",
38     TRASHED_COLLECTION = "TrashedCollection",
39     PROCESS = "Process",
40     RUNNING_PROCESS_ADMIN = "RunningProcessAdmin",
41     PROCESS_ADMIN = "ProcessAdmin",
42     RUNNING_PROCESS_RESOURCE = "RunningProcessResource",
43     PROCESS_RESOURCE = "ProcessResource",
44     READONLY_PROCESS_RESOURCE = "ReadOnlyProcessResource",
45     PROCESS_LOGS = "ProcessLogs",
46     REPOSITORY = "Repository",
47     SSH_KEY = "SshKey",
48     VIRTUAL_MACHINE = "VirtualMachine",
49     KEEP_SERVICE = "KeepService",
50     USER = "User",
51     USER_DETAILS = "UserDetails",
52     GROUPS = "Group",
53     GROUP_MEMBER = "GroupMember",
54     PERMISSION_EDIT = "PermissionEdit",
55     LINK = "Link",
56     WORKFLOW = "Workflow",
57     READONLY_WORKFLOW = "ReadOnlyWorkflow",
58     SEARCH_RESULTS = "SearchResults",
59     MULTI = "Multi",
60 }
61
62 const processOrder = [
63     ContextMenuActionNames.VIEW_DETAILS,
64     ContextMenuActionNames.OPEN_IN_NEW_TAB,
65     ContextMenuActionNames.COPY_UUID,
66     ContextMenuActionNames.COPY_AND_RERUN_PROCESS,
67     ContextMenuActionNames.CANCEL,
68     ContextMenuActionNames.EDIT_PROCESS,
69     ContextMenuActionNames.REMOVE,
70     ContextMenuActionNames.DIVIDER,
71     ContextMenuActionNames.OUTPUTS,
72     ContextMenuActionNames.ADD_TO_FAVORITES,
73     ContextMenuActionNames.ADD_TO_PUBLIC_FAVORITES,
74     ContextMenuActionNames.DIVIDER,
75     ContextMenuActionNames.COPY_LINK_TO_CLIPBOARD,
76     ContextMenuActionNames.API_DETAILS,
77 ];
78
79 const projectOrder = [
80     ContextMenuActionNames.VIEW_DETAILS,
81     ContextMenuActionNames.OPEN_IN_NEW_TAB,
82     ContextMenuActionNames.COPY_UUID,
83     ContextMenuActionNames.SHARE,
84     ContextMenuActionNames.EDIT_PROJECT,
85     ContextMenuActionNames.MOVE_TO_TRASH,
86     ContextMenuActionNames.DIVIDER,
87     ContextMenuActionNames.NEW_PROJECT,
88     ContextMenuActionNames.MOVE_TO,
89     ContextMenuActionNames.FREEZE_PROJECT,
90     ContextMenuActionNames.ADD_TO_FAVORITES,
91     ContextMenuActionNames.ADD_TO_PUBLIC_FAVORITES,
92     ContextMenuActionNames.DIVIDER,
93     ContextMenuActionNames.COPY_LINK_TO_CLIPBOARD,
94     ContextMenuActionNames.OPEN_WITH_3RD_PARTY_CLIENT,
95     ContextMenuActionNames.API_DETAILS,
96 ];
97
98 const collectionOrder = [
99     ContextMenuActionNames.VIEW_DETAILS,
100     ContextMenuActionNames.OPEN_IN_NEW_TAB,
101     ContextMenuActionNames.COPY_UUID,
102     ContextMenuActionNames.SHARE,
103     ContextMenuActionNames.EDIT_COLLECTION,
104     ContextMenuActionNames.MOVE_TO_TRASH,
105     ContextMenuActionNames.DIVIDER,
106     ContextMenuActionNames.MAKE_A_COPY,
107     ContextMenuActionNames.MOVE_TO,
108     ContextMenuActionNames.ADD_TO_FAVORITES,
109     ContextMenuActionNames.ADD_TO_PUBLIC_FAVORITES,
110     ContextMenuActionNames.DIVIDER,
111     ContextMenuActionNames.COPY_LINK_TO_CLIPBOARD,
112     ContextMenuActionNames.OPEN_WITH_3RD_PARTY_CLIENT,
113     ContextMenuActionNames.API_DETAILS,
114 ];
115
116 const workflowOrder = [
117     ContextMenuActionNames.VIEW_DETAILS,
118     ContextMenuActionNames.OPEN_IN_NEW_TAB,
119     ContextMenuActionNames.COPY_UUID,
120     ContextMenuActionNames.RUN_WORKFLOW,
121     ContextMenuActionNames.DELETE_WORKFLOW,
122     ContextMenuActionNames.DIVIDER,
123     ContextMenuActionNames.COPY_LINK_TO_CLIPBOARD,
124     ContextMenuActionNames.API_DETAILS,
125 ]
126
127 const rootProjectOrder = [
128     ContextMenuActionNames.VIEW_DETAILS,
129     ContextMenuActionNames.USER_ACCOUNT,
130     ContextMenuActionNames.API_DETAILS,
131 ];
132
133 const defaultMultiOrder = [
134     ContextMenuActionNames.MOVE_TO,
135     ContextMenuActionNames.MAKE_A_COPY,
136     ContextMenuActionNames.MOVE_TO_TRASH,
137 ];
138
139 const kindToOrder: Record<string, ContextMenuActionNames[]> = {
140     [ContextMenuKind.MULTI]: defaultMultiOrder,
141
142     [ContextMenuKind.PROCESS]: processOrder,
143     [ContextMenuKind.PROCESS_ADMIN]: processOrder,
144     [ContextMenuKind.PROCESS_RESOURCE]: processOrder,
145     [ContextMenuKind.RUNNING_PROCESS_ADMIN]: processOrder,
146     [ContextMenuKind.RUNNING_PROCESS_RESOURCE]: processOrder,
147
148     [ContextMenuKind.PROJECT]: projectOrder,
149     [ContextMenuKind.PROJECT_ADMIN]: projectOrder,
150     [ContextMenuKind.FROZEN_PROJECT]: projectOrder,
151     [ContextMenuKind.FROZEN_PROJECT_ADMIN]: projectOrder,
152
153     [ContextMenuKind.COLLECTION]: collectionOrder,
154     [ContextMenuKind.COLLECTION_ADMIN]: collectionOrder,
155     [ContextMenuKind.READONLY_COLLECTION]: collectionOrder,
156     [ContextMenuKind.OLD_VERSION_COLLECTION]: collectionOrder,
157
158     [ContextMenuKind.WORKFLOW]: workflowOrder,
159     [ContextMenuKind.READONLY_WORKFLOW]: workflowOrder,
160
161     [ContextMenuKind.GROUPS]: projectOrder,
162
163     [ContextMenuKind.FILTER_GROUP]: projectOrder,
164     [ContextMenuKind.FILTER_GROUP_ADMIN]: projectOrder,
165
166     [ContextMenuKind.ROOT_PROJECT]: rootProjectOrder,
167     [ContextMenuKind.ROOT_PROJECT_ADMIN]: rootProjectOrder,
168 };
169
170 export const menuDirection = {
171     VERTICAL: 'vertical',
172     HORIZONTAL: 'horizontal'
173 }
174
175 export const sortMenuItems = (menuKind: ContextMenuKind, menuItems: ContextMenuAction[], orthagonality: string): ContextMenuAction[] | MultiSelectMenuAction[] => {
176
177     const preferredOrder = kindToOrder[menuKind];
178     //if no specified order, sort by name
179     if (!preferredOrder) return menuItems.sort(sortByProperty("name"));
180
181     const bucketMap = new Map();
182     const leftovers: ContextMenuAction[] = [];
183
184     // if we have multiple dividers, we need each of them to have a different "name" property
185     let count = 0;
186
187     preferredOrder.forEach((name) => {
188         if (name === ContextMenuActionNames.DIVIDER) {
189             count++;
190             bucketMap.set(`${name}-${count}`, orthagonality === menuDirection.VERTICAL ? verticalMenuDivider : horizontalMenuDivider)
191         } else {
192             bucketMap.set(name, null)
193         }
194     });
195     [...menuItems].forEach((item) => {
196         if (bucketMap.has(item.name)) bucketMap.set(item.name, item);
197         else leftovers.push(item);
198     });
199
200     return Array.from(bucketMap.values()).concat(leftovers).filter((item) => item !== null).reduce((acc, val)=>{
201         return acc.at(-1)?.name === "Divider" && val.name === "Divider" ? acc : acc.concat(val)
202     }, []);
203 };