storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/browser/app/js/objects/Path.js (about)

     1  /*
     2   * MinIO Cloud Storage (C) 2016, 2018, 2019 MinIO, Inc.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  import React from "react"
    18  import { connect } from "react-redux"
    19  import ClickOutHandler from "react-onclickout"
    20  import { OverlayTrigger, Tooltip } from "react-bootstrap"
    21  import { getCurrentBucket } from "../buckets/selectors"
    22  import * as actionsObjects from "./actions"
    23  import * as actionsBuckets from "../buckets/actions"
    24  
    25  export class Path extends React.Component {
    26    constructor(props) {
    27      super(props)
    28      this.state = {
    29        isEditing: false,
    30        path: ""
    31      }
    32    }
    33    stopEditing() {
    34      this.setState({
    35        isEditing: false
    36      })
    37    }
    38    onPrefixClick(e, prefix) {
    39      e.preventDefault()
    40      const { selectPrefix } = this.props
    41      selectPrefix(prefix)
    42    }
    43    onEditClick(e) {
    44      e.preventDefault()
    45      const { currentBucket, currentPrefix } = this.props
    46      this.setState(
    47        {
    48          isEditing: true,
    49          path: `${currentBucket}/${currentPrefix}`
    50        },
    51        () => {
    52          // focus on input and move cursor to the end
    53          this.pathInput.focus()
    54          this.pathInput.setSelectionRange(
    55            this.state.path.length,
    56            this.state.path.length
    57          )
    58        }
    59      )
    60    }
    61    onKeyDown(e) {
    62      // When Esc key is pressed
    63      if (e.keyCode === 27) {
    64        this.stopEditing()
    65      }
    66    }
    67    onInputClickOut() {
    68      this.stopEditing()
    69    }
    70    bucketExists(bucketName) {
    71      const { buckets } = this.props
    72      return buckets.includes(bucketName)
    73    }
    74    async onSubmit(e) {
    75      e.preventDefault()
    76      const { makeBucket, selectBucket } = this.props
    77      // all paths need to end in slash to display contents properly
    78      let path = this.state.path
    79      if (!path.endsWith("/")) {
    80        path += "/"
    81      }
    82      const splittedPath = path.split("/")
    83      if (splittedPath.length > 0) {
    84        // prevent bucket name from being empty
    85        if (splittedPath[0]) {
    86          const bucketName = splittedPath[0]
    87          const prefix = splittedPath.slice(1).join("/")
    88          if (!this.bucketExists(bucketName)) {
    89            await makeBucket(bucketName)
    90          }
    91          // check updated buckets and don't proceed on invalid inputs
    92          if (this.bucketExists(bucketName)) {
    93            // then select bucket with prefix
    94            selectBucket(bucketName, prefix)
    95          }
    96          this.stopEditing()
    97        }
    98      }
    99    }
   100    render() {
   101      const pathTooltip = <Tooltip id="tt-path">Choose or create new path</Tooltip>
   102      const { currentBucket, currentPrefix } = this.props
   103      let dirPath = []
   104      let path = ""
   105      if (currentPrefix) {
   106        path = currentPrefix.split("/").map((dir, i) => {
   107          if (dir) {
   108            dirPath.push(dir)
   109            let dirPath_ = dirPath.join("/") + "/"
   110            return (
   111              <span key={i}>
   112                <a href="" onClick={e => this.onPrefixClick(e, dirPath_)}>
   113                  {dir}
   114                </a>
   115              </span>
   116            )
   117          }
   118        })
   119      }
   120      return (
   121        <h2>
   122          {this.state.isEditing ? (
   123            <ClickOutHandler onClickOut={() => this.onInputClickOut()}>
   124              <form onSubmit={e => this.onSubmit(e)}>
   125                <input
   126                  className="form-control form-control--path"
   127                  type="text"
   128                  placeholder="Choose or create new path"
   129                  ref={node => (this.pathInput = node)}
   130                  onKeyDown={e => this.onKeyDown(e)}
   131                  value={this.state.path}
   132                  onChange={e => this.setState({ path: e.target.value })}
   133                />
   134              </form>
   135            </ClickOutHandler>
   136          ) : (
   137            <React.Fragment>
   138              <span className="main">
   139                <a href="" onClick={e => this.onPrefixClick(e, "")}>
   140                  {currentBucket}
   141                </a>
   142              </span>
   143              {path}
   144              <OverlayTrigger placement="bottom" overlay={pathTooltip}>
   145                <a href="" onClick={e => this.onEditClick(e)} className="fe-edit">
   146                  <i className="fas fa-folder-plus" />
   147                </a>
   148              </OverlayTrigger>
   149            </React.Fragment>
   150          )}
   151        </h2>
   152      )
   153    }
   154  }
   155  
   156  const mapStateToProps = state => {
   157    return {
   158      buckets: state.buckets.list,
   159      currentBucket: getCurrentBucket(state),
   160      currentPrefix: state.objects.currentPrefix
   161    }
   162  }
   163  
   164  const mapDispatchToProps = dispatch => {
   165    return {
   166      makeBucket: bucket => dispatch(actionsBuckets.makeBucket(bucket)),
   167      selectBucket: (bucket, prefix) =>
   168        dispatch(actionsBuckets.selectBucket(bucket, prefix)),
   169      selectPrefix: prefix => dispatch(actionsObjects.selectPrefix(prefix))
   170    }
   171  }
   172  
   173  export default connect(
   174    mapStateToProps,
   175    mapDispatchToProps
   176  )(Path)