Skip to content
Snippets Groups Projects
LeftToolbar.jsx 11.4 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*
     *  Copyright ETH 2023 Zürich, Scientific IT Services
     *
     *  Licensed under the Apache License, Version 2.0 (the "License");
     *  you may not use this file except in compliance with the License.
     *  You may obtain a copy of the License at
     *
     *       http://www.apache.org/licenses/LICENSE-2.0
     *
     *  Unless required by applicable law or agreed to in writing, software
     *  distributed under the License is distributed on an "AS IS" BASIS,
     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     *  See the License for the specific language governing permissions and
     *  limitations under the License.
     *
     */
    
    
    import React from 'react'
    
    import ResizeObserver from 'rc-resize-observer'
    
    import Button from '@material-ui/core/Button'
    import CreateNewFolderIcon from '@material-ui/icons/CreateNewFolderOutlined'
    
    import DownloadIcon from '@material-ui/icons/GetApp'
    import DeleteIcon from '@material-ui/icons/Delete'
    
    import RenameIcon from '@material-ui/icons/Create'
    import CopyIcon from '@material-ui/icons/FileCopy'
    import MoveIcon from '@material-ui/icons/ArrowRightAlt'
    
    import MoreIcon from '@material-ui/icons/MoreVert'
    
    import messages from '@src/js/common/messages.js'
    import { withStyles } from '@material-ui/core/styles'
    
    import logger from '@src/js/common/logger.js'
    import autoBind from 'auto-bind'
    import IconButton from '@material-ui/core/IconButton'
    import { debounce } from '@material-ui/core'
    import Container from '@src/js/components/common/form/Container.jsx'
    import Popover from '@material-ui/core/Popover'
    
    import InputDialog from '@src/js/components/common/dialog/InputDialog.jsx'
    
    import ConfirmationDialog from '@src/js/components/common/dialog/ConfirmationDialog.jsx'
    import LocationDialog from '@src/js/components/database/data-browser/LocationDialog.jsx'
    
    import LoadingDialog from '@src/js/components/common/loading/LoadingDialog.jsx'
    
    const color = 'default'
    
    const moveLocationMode = 'move'
    const copyLocationMode = 'copy'
    
    
    const styles = theme => ({
      buttons: {
    
        display: 'flex',
        alignItems: 'center',
    
        whiteSpace: 'nowrap',
    
        '&>button': {
          marginRight: theme.spacing(1)
        },
        '&>button:nth-last-child(1)': {
          marginRight: 0
        }
    
      toggleButton: {},
      collapsedButtonsContainer: {
        display: 'flex',
        flexDirection: 'column',
        '&>button': {
          marginBottom: theme.spacing(1)
        },
        '&>button:nth-last-child(1)': {
          marginBottom: 0
        }
      },
    
    })
    
    class LeftToolbar extends React.Component {
    
      constructor(props, context) {
        super(props, context)
        autoBind(this)
    
    
          hiddenButtonsPopup: null,
    
          newFolderDialogOpen: false,
    
          deleteDialogOpen: false,
    
          renameDialogOpen: false,
    
          locationDialogMode: null,
          loading: false
    
        this.controller = this.props.controller
    
        this.onResize = debounce(this.onResize, 1)
      }
    
    
      async handleNewFolderCreate(folderName) {
    
        this.closeNewFolderDialog()
    
        await this.controller.createNewFolder(folderName)
    
      handleNewFolderCancel() {
        this.closeNewFolderDialog()
      }
    
    
      openNewFolderDialog() {
        this.setState({ newFolderDialogOpen: true })
      }
    
      closeNewFolderDialog() {
        this.setState({ newFolderDialogOpen: false })
    
      openDeleteDialog() {
        this.setState({ deleteDialogOpen: true })
      }
    
      closeDeleteDialog() {
        this.setState({ deleteDialogOpen: false })
      }
    
    
      openRenameDialog() {
        this.setState({ renameDialogOpen: true })
      }
    
      closeRenameDialog() {
        this.setState({ renameDialogOpen: false })
      }
    
      async handleRenameConfirm(newName) {
        const { multiselectedFiles } = this.props
        const oldName = multiselectedFiles.values().next().value.name
        this.closeRenameDialog()
    
    
        try {
          this.setState({ loading: true })
          await this.controller.rename(oldName, newName)
        } finally {
          this.setState({ loading: false })
        }
    
      }
    
      handleRenameCancel() {
        this.closeRenameDialog()
      }
    
    
      openMoveLocationDialog() {
        this.setState({ locationDialogMode: moveLocationMode })
      }
    
      openCopyLocationDialog() {
        this.setState({ locationDialogMode: copyLocationMode })
      }
    
      closeLocationDialog() {
        this.setState({ locationDialogMode: null })
      }
    
    
      async handleLocationConfirm(newPath) {
        const { multiselectedFiles } = this.props
        const { locationDialogMode} = this.state
    
        this.closeLocationDialog()
    
        try {
          this.setState({ loading: true })
          if (locationDialogMode === moveLocationMode) {
            await this.controller.move(multiselectedFiles, newPath)
          } else {
            await this.controller.copy(multiselectedFiles, newPath)
          }
        } finally {
          this.setState({ loading: false })
    
      }
    
      handleLocationCancel() {
        this.closeLocationDialog()
      }
    
    
      async handleDeleteConfirm() {
        const { multiselectedFiles } = this.props
    
        this.closeDeleteDialog()
    
        await this.controller.delete(multiselectedFiles)
    
      }
    
      handleDeleteCancel() {
        this.closeDeleteDialog()
      }
    
    
      renderNoSelectionContextToolbar() {
        const { classes, buttonSize } = this.props
    
            key='new-folder'
    
            classes={{ root: classes.button }}
            color={color}
            size={buttonSize}
            variant='outlined'
            startIcon={<CreateNewFolderIcon />}
    
            onClick={this.openNewFolderDialog}
    
          </Button>,
          <InputDialog
            key='new-folder-dialog'
            open={this.state.newFolderDialogOpen}
            title={messages.get(messages.NEW_FOLDER)}
            inputLabel={messages.get(messages.FOLDER_NAME)}
    
            onCancel={this.handleNewFolderCancel}
    
            onConfirm={this.handleNewFolderCreate}
            />
        ])
    
        const {
          classes,
          buttonSize,
          multiselectedFiles,
          datastoreServer,
          sessionToken,
    
        } = this.props
    
        const {
          width,
          hiddenButtonsPopup,
          deleteDialogOpen,
          renameDialogOpen,
          locationDialogMode
        } = this.state
    
    
        const ellipsisButtonSize = 24
        const buttonsCount = 5
        const minSize = 500
        const roughButtonSize = Math.floor(minSize / buttonsCount)
        const hideButtons = width < minSize
    
        const visibleButtonsCount = Math.max(hideButtons ? Math.floor((width - 3 * ellipsisButtonSize) / roughButtonSize) : buttonsCount, 0)
    
            classes={{ root: classes.button }}
            color={color}
            size={buttonSize}
            variant='outlined'
    
            disabled={multiselectedFiles.size !== 1 || multiselectedFiles.values().next().value.directory}
    
          >
            {messages.get(messages.DOWNLOAD)}
          </Button>,
          <Button
    
            classes={{ root: classes.button }}
            color={color}
            size={buttonSize}
            variant='text'
            startIcon={<DeleteIcon />}
    
            onClick={this.openDeleteDialog}
    
          >
            {messages.get(messages.DELETE)}
          </Button>,
          <Button
    
            classes={{ root: classes.button }}
            color={color}
            size={buttonSize}
            variant='text'
    
            disabled={multiselectedFiles.size !== 1}
    
            onClick={this.openRenameDialog}
    
          >
            {messages.get(messages.RENAME)}
          </Button>,
          <Button
    
            classes={{ root: classes.button }}
            color={color}
            size={buttonSize}
            variant='text'
            startIcon={<CopyIcon />}
    
            onClick={this.openCopyLocationDialog}
    
          >
            {messages.get(messages.COPY)}
          </Button>,
          <Button
    
            classes={{ root: classes.button }}
            color={color}
            size={buttonSize}
            variant='text'
            startIcon={<MoveIcon />}
    
            onClick={this.openMoveLocationDialog}
    
          >
            {messages.get(messages.MOVE)}
          </Button>
        ]
        const ellipsisButton = (
          <IconButton
    
            classes={{ root: classes.button }}
            color={color}
            size={iconButtonSize}
    
            variant='outlined'
            onClick={this.handleOpen}
    
            open={Boolean(hiddenButtonsPopup)}
            anchorEl={hiddenButtonsPopup}
            onClose={this.handleClose}
            anchorOrigin={{
              vertical: 'bottom',
              horizontal: 'left'
            }}
            transformOrigin={{
              vertical: 'top',
              horizontal: 'left'
            }}
          >
            <Container square={true}>{this.renderCollapsedButtons(buttons.slice(visibleButtonsCount))}</Container>
          </Popover>
        )
    
        const selectedValue = multiselectedFiles.values().next().value;
    
        return (
          <div className={classes.buttons}>
    
              ? [...buttons.slice(0, visibleButtonsCount), ellipsisButton, popover]
    
            <ConfirmationDialog
    
              key='delete-dialog'
    
              open={deleteDialogOpen}
              onConfirm={this.handleDeleteConfirm}
              onCancel={this.handleDeleteCancel}
              title={messages.get(messages.DELETE)}
              content={messages.get(messages.CONFIRMATION_DELETE_SELECTED)}
            />
    
            <InputDialog
              key='rename-dialog'
    
              title={selectedValue.directory ? messages.get(messages.RENAME_FOLDER) : messages.get(messages.RENAME_FILE)}
              inputLabel={selectedValue.directory ? messages.get(messages.FOLDER_NAME) : messages.get(messages.FILE_NAME)}
              inputValue={selectedValue.name}
              onCancel={this.handleRenameCancel}
              onConfirm={this.handleRenameConfirm}
            />
    
            <LocationDialog
              key='location-dialog'
    
              open={!!locationDialogMode}
              title={locationDialogMode === moveLocationMode ? messages.get(messages.MOVE) : messages.get(messages.COPY)}
    
              content={messages.get(messages.FILE_OR_FILES, multiselectedFiles.size)}
    
              datastoreServer={datastoreServer}
              sessionToken={sessionToken}
    
              path={path}
    
              multiselectedFiles={multiselectedFiles}
    
              onCancel={this.handleLocationCancel}
              onConfirm={this.handleLocationConfirm}
              />
    
      renderCollapsedButtons(buttons) {
        const { classes } = this.props
        return (
          <div className={classes.collapsedButtonsContainer}>
            {buttons}
          </div>
        )
      }
    
    
      onResize({ width }) {
        if (width !== this.state.width) {
    
          this.setState({ width, hiddenButtonsPopup: null })
    
      handleOpen(event) {
        this.setState({
          hiddenButtonsPopup: event.currentTarget
        })
      }
    
      handleClose() {
        this.setState({
          hiddenButtonsPopup: null
        })
      }
    
    
      render() {
        logger.log(logger.DEBUG, 'LeftToolbar.render')
    
    
        const { multiselectedFiles, classes, owner } = this.props
    
        const { loading } = this.state
    
        return ([
          <ResizeObserver key='resize-observer' onResize={this.onResize}>
    
            <div className={classes.buttons}>
              {multiselectedFiles && multiselectedFiles.size > 0
                ? this.renderSelectionContextToolbar()
                : this.renderNoSelectionContextToolbar()}
            </div>
    
          <LoadingDialog key='loaging-dialog' variant='indeterminate' loading={loading} />
    
    }
    
    export default withStyles(styles)(LeftToolbar)