X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/781c06ee0633709137ba3a4d305eb6e1db2a6dfb..e186e23688c92f91e4cbf564dee5018789e0b8ad:/src/components/multiselectToolbar/MultiselectToolbar.tsx diff --git a/src/components/multiselectToolbar/MultiselectToolbar.tsx b/src/components/multiselectToolbar/MultiselectToolbar.tsx index 98144a54dc..22a9c7139c 100644 --- a/src/components/multiselectToolbar/MultiselectToolbar.tsx +++ b/src/components/multiselectToolbar/MultiselectToolbar.tsx @@ -2,44 +2,39 @@ // // SPDX-License-Identifier: AGPL-3.0 -import React from 'react'; -import { connect } from 'react-redux'; -import { StyleRulesCallback, withStyles, WithStyles, Toolbar, Tooltip, IconButton } from '@material-ui/core'; -import { ArvadosTheme } from 'common/custom-theme'; -import { RootState } from 'store/store'; -import { Dispatch } from 'redux'; -import { TCheckedList } from 'components/data-table/data-table'; -import { openRemoveProcessDialog, openRemoveManyProcessesDialog } from 'store/processes/processes-actions'; -import { processResourceActionSet } from '../../views-components/context-menu/action-sets/process-resource-action-set'; -import { ContextMenuResource } from 'store/context-menu/context-menu-actions'; -import { extractUuidKind } from 'models/resource'; -import { openMoveProcessDialog } from 'store/processes/process-move-actions'; -import { openCopyProcessDialog, openCopyManyProcessesDialog } from 'store/processes/process-copy-actions'; -import { getResource } from 'store/resources/resources'; -import { ResourcesState } from 'store/resources/resources'; -import { getProcess } from 'store/processes/process'; -import { CopyProcessDialog, CopyManyProcessesDialog } from 'views-components/dialog-forms/copy-process-dialog'; -import { collectionActionSet } from 'views-components/context-menu/action-sets/collection-action-set'; -import { ContextMenuAction, ContextMenuActionSet } from 'views-components/context-menu/context-menu-action-set'; -import { TrashIcon } from 'components/icon/icon'; -import { multiselectActionsFilters, TMultiselectActionsFilters } from './ms-toolbar-action-filters'; - -type CssRules = 'root' | 'button'; +import React from "react"; +import { connect } from "react-redux"; +import { StyleRulesCallback, withStyles, WithStyles, Toolbar, Tooltip, IconButton } from "@material-ui/core"; +import { ArvadosTheme } from "common/custom-theme"; +import { RootState } from "store/store"; +import { Dispatch } from "redux"; +import { TCheckedList } from "components/data-table/data-table"; +import { ContextMenuResource } from "store/context-menu/context-menu-actions"; +import { Resource, extractUuidKind } from "models/resource"; +import { getResource } from "store/resources/resources"; +import { ResourcesState } from "store/resources/resources"; +import { ContextMenuAction, ContextMenuActionSet } from "views-components/context-menu/context-menu-action-set"; +import { RestoreFromTrashIcon, TrashIcon } from "components/icon/icon"; +import { multiselectActionsFilters, TMultiselectActionsFilters, contextMenuActionConsts } from "./ms-toolbar-action-filters"; +import { kindToActionSet, findActionByName } from "./ms-kind-action-differentiator"; +import { toggleTrashAction } from "views-components/context-menu/action-sets/project-action-set"; +import { copyToClipboardAction } from "store/open-in-new-tab/open-in-new-tab.actions"; + +type CssRules = "root" | "button"; const styles: StyleRulesCallback = (theme: ArvadosTheme) => ({ root: { - display: 'flex', - flexDirection: 'row', + display: "flex", + flexDirection: "row", width: 0, padding: 0, - margin: '1rem auto auto 0.5rem', - overflow: 'hidden', - transition: 'width 150ms', - borderBottom: '1px solid gray', + margin: "1rem auto auto 0.5rem", + overflow: "hidden", + transition: "width 150ms", }, button: { - width: '1rem', - margin: 'auto 5px', + width: "1rem", + margin: "auto 5px", }, }); @@ -47,7 +42,7 @@ export type MultiselectToolbarProps = { isVisible: boolean; checkedList: TCheckedList; resources: ResourcesState; - executeMulti: (fn, checkedList: TCheckedList, resources: ResourcesState) => void; + executeMulti: (action: ContextMenuAction, checkedList: TCheckedList, resources: ResourcesState) => void; }; export const MultiselectToolbar = connect( @@ -58,26 +53,36 @@ export const MultiselectToolbar = connect( const { classes, checkedList } = props; const currentResourceKinds = Array.from(selectedToKindSet(checkedList)); - const buttons = selectActionsByKind(currentResourceKinds, multiselectActionsFilters); + const currentPathIsTrash = window.location.pathname === "/trash"; + const buttons = + currentPathIsTrash && selectedToKindSet(checkedList).size + ? [toggleTrashAction] + : selectActionsByKind(currentResourceKinds, multiselectActionsFilters); return ( - + {buttons.length ? ( buttons.map((btn, i) => - btn.name === 'ToggleTrashAction' ? ( - - props.executeMulti(btn.execute, checkedList, props.resources)} - > - + btn.name === "ToggleTrashAction" ? ( + + props.executeMulti(btn, checkedList, props.resources)}> + {currentPathIsTrash ? : } ) : ( - - props.executeMulti(btn.execute, checkedList, props.resources)} - > - {btn.icon ? btn.icon({}) : <>error} + + props.executeMulti(btn, checkedList, props.resources)}> + {btn.icon ? btn.icon({}) : <>} ) @@ -90,8 +95,7 @@ export const MultiselectToolbar = connect( }) ); -//todo: put these all in a /utils -function selectedToArray(checkedList: TCheckedList): Array { +export function selectedToArray(checkedList: TCheckedList): Array { const arrayifiedSelectedList: Array = []; for (const [key, value] of Object.entries(checkedList)) { if (value === true) { @@ -101,7 +105,7 @@ function selectedToArray(checkedList: TCheckedList): Array { return arrayifiedSelectedList; } -function selectedToKindSet(checkedList: TCheckedList): Set { +export function selectedToKindSet(checkedList: TCheckedList): Set { const setifiedList = new Set(); for (const [key, value] of Object.entries(checkedList)) { if (value === true) { @@ -111,18 +115,61 @@ function selectedToKindSet(checkedList: TCheckedList): Set { return setifiedList; } -//num of currentResourceKinds * num of actions (in ContextMenuActionSet) * num of filters -//worst case: 14 * x * x -oof -function filterActions(actionArray: ContextMenuActionSet, filters: Array): Array { - return actionArray[0].filter((action) => filters.includes(action.name as string)); +function groupByKind(checkedList: TCheckedList, resources: ResourcesState): Record { + const result = {}; + selectedToArray(checkedList).forEach(uuid => { + const resource = getResource(uuid)(resources) as Resource; + if (!result[resource.kind]) result[resource.kind] = []; + result[resource.kind].push(resource); + }); + return result; +} + +function filterActions(actionArray: ContextMenuActionSet, filters: Set): Array { + return actionArray[0].filter(action => filters.has(action.name as string)); } function selectActionsByKind(currentResourceKinds: Array, filterSet: TMultiselectActionsFilters) { - const result: Array = []; - currentResourceKinds.forEach((kind) => { - if (filterSet[kind]) result.push(...filterActions(...filterSet[kind])); + const rawResult: Set = new Set(); + const resultNames = new Set(); + const allFiltersArray: ContextMenuAction[][] = []; + currentResourceKinds.forEach(kind => { + if (filterSet[kind]) { + const actions = filterActions(...filterSet[kind]); + allFiltersArray.push(actions); + actions.forEach(action => { + if (!resultNames.has(action.name)) { + rawResult.add(action); + resultNames.add(action.name); + } + }); + } + }); + + const filteredNameSet = allFiltersArray.map(filterArray => { + const resultSet = new Set(); + filterArray.forEach(action => resultSet.add(action.name || "")); + return resultSet; + }); + + const filteredResult = Array.from(rawResult).filter(action => { + for (let i = 0; i < filteredNameSet.length; i++) { + if (!filteredNameSet[i].has(action.name)) return false; + } + return true; + }); + + return filteredResult.sort((a, b) => { + const nameA = a.name || ""; + const nameB = b.name || ""; + if (nameA < nameB) { + return -1; + } + if (nameA > nameB) { + return 1; + } + return 0; }); - return result; } //--------------------------------------------------// @@ -138,9 +185,25 @@ function mapStateToProps(state: RootState) { function mapDispatchToProps(dispatch: Dispatch) { return { - executeMulti: (fn, checkedList: TCheckedList, resources: ResourcesState) => - selectedToArray(checkedList).forEach((uuid) => { - fn(dispatch, getResource(uuid)(resources)); - }), + executeMulti: (selectedAction: ContextMenuAction, checkedList: TCheckedList, resources: ResourcesState): void => { + const kindGroups = groupByKind(checkedList, resources); + switch (selectedAction.name) { + case contextMenuActionConsts.MOVE_TO: + const firstResource = getResource(selectedToArray(checkedList)[0])(resources) as Resource; + const action = findActionByName(selectedAction.name as string, kindToActionSet[firstResource.kind]); + if (action) action.execute(dispatch, kindGroups[firstResource.kind]); + break; + case contextMenuActionConsts.COPY_TO_CLIPBOARD: + const selectedResources = selectedToArray(checkedList).map(uuid => getResource(uuid)(resources)); + dispatch(copyToClipboardAction(selectedResources)); + break; + default: + for (const kind in kindGroups) { + const action = findActionByName(selectedAction.name as string, kindToActionSet[kind]); + if (action) action.execute(dispatch, kindGroups[kind]); + } + break; + } + }, }; }