Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
File.js 3.45 KiB
/* eslint-disable react/require-default-props */

import React from 'react'
import { last } from 'lodash'
import PropTypes from 'prop-types'
import styled from 'styled-components'
import { th } from '@pubsweet/ui-toolkit'
import { withProps, withHandlers, compose } from 'recompose'

import { Label, IconButton, Text } from './'
import { marginHelper } from './styledHelpers'

const parseFileSize = size => {
  const kbSize = size / 1000
  const mbSize = kbSize / 1000
  const gbSize = mbSize / 1000

  if (Math.floor(gbSize)) {
    return `${Math.floor(gbSize)} GB`
  } else if (Math.floor(mbSize)) {
    return `${Math.floor(mbSize)} MB`
  } else if (Math.floor(kbSize)) {
    return `${Math.floor(kbSize)} kB`
  }
  return `${size} bytes`
}

const hasPreview = (name = '') => {
  const extension = last(name.split('.')).toLocaleLowerCase()
  return ['pdf', 'png', 'jpg'].includes(extension)
}

const FileItem = ({
  fileSize,
  onPreview,
  item: file,
  hasPreview,
  hasDelete,
  onDownload,
  onDelete,
  dragHandle = null,
  ...rest
}) => (
  <Root data-test-id={`file-${file.id}`} {...rest}>
    {typeof dragHandle === 'function' ? dragHandle() : dragHandle}
    <FileInfo>
      <Text mr={1} secondary whiteSpace="nowrap">
        {file.name}
      </Text>
      <Label>{fileSize}</Label>
    </FileInfo>
    {hasPreview && (
      <IconButton
        icon="eye"
        iconSize={2}
        ml={1}
        mr={1}
        onClick={onPreview}
        secondary
      />
    )}
    <IconButton
      icon="download"
      iconSize={2}
      ml={hasPreview ? 0 : 1}
      mr={1}
      onClick={onDownload}
      secondary
    />
    {hasDelete && (
      <IconButton
        icon="trash"
        iconSize={2}
        mr={1}
        onClick={onDelete}
        secondary
      />
    )}
  </Root>
)

FileItem.propTypes = {
  /** The file. */
  item: PropTypes.shape({
    id: PropTypes.string,
    name: PropTypes.string,
    size: PropTypes.number,
  }).isRequired,
  /** Used when part of a sortable list. */
  dragHandle: PropTypes.oneOfType([PropTypes.element, PropTypes.func]),
  /** Handler for the preview button. */
  onPreview: PropTypes.func,
  /** Handler for the download button. */
  onDownload: PropTypes.func,
  /** Handler for the delete button. */
  onDelete: PropTypes.func,
}

export default compose(
  withProps(({ item: { name, size }, onDelete }) => ({
    hasPreview: hasPreview(name),
    hasDelete: !!onDelete,
    fileSize: parseFileSize(size),
  })),
  withHandlers({
    onDownload: ({ onDownload, item }) => () => {
      typeof onDownload === 'function' && onDownload(item)
    },
    onPreview: ({ onPreview, item }) => () => {
      typeof onPreview === 'function' && onPreview(item)
    },
    onDelete: ({ onDelete, item }) => () => {
      typeof onDelete === 'function' && onDelete(item)
    },
  }),
)(FileItem)

// #region styles
const Root = styled.div`
  align-items: center;
  background-color: ${th('colorBackgroundHue')};
  box-shadow: ${({ shadow }) => (shadow ? th('boxShadow') : 'none')};
  border-radius: ${th('borderRadius')};
  display: flex;
  border: ${th('borderWidth')} ${th('borderStyle')} ${th('colorBorder')};
  height: calc(${th('gridUnit')} * 5);
  white-space: nowrap;
  ${marginHelper};
`

const FileInfo = styled.div`
  align-items: center;
  display: flex;
  flex: 1;
  justify-content: space-between;
  height: calc(${th('gridUnit')} * 5);
  padding: 0 ${th('gridUnit')};
  border-right: ${th('borderWidth')} ${th('borderStyle')} ${th('colorBorder')};
`
// #endregion