UNPKG

@bigfishtv/cockpit

Version:

225 lines (202 loc) 6.69 kB
import PropTypes from 'prop-types' import React, { Component } from 'react' import { DragSource } from 'react-dnd' import classnames from 'classnames' import _throttle from 'lodash/debounce' import ImageCell from '../asset/ImageCell' import VideoCell from '../asset/VideoCell' import AudioCell from '../asset/AudioCell' import DocumentCell from '../asset/DocumentCell' import UnknownFileCell from '../asset/UnknownFileCell' import AssetCellToolbar from '../asset/AssetCellToolbar' import * as DragTypes from '../../constants/DragTypes' import { getAssetUrl, getImageUrl } from '../../utils/fileUtils' import { connect } from 'react-redux' import { cancelGeneratedImageRequest, requestGeneratedImage, generatedImageRequestFulfilled, } from '../../actions/imageRequestQueue' const dragSource = { beginDrag(props) { return { id: props.asset.id, } }, } // we define this because react-docgen fails when defaultProp directly references an imported component const DefaultAssetCellToolbar = props => <AssetCellToolbar {...props} /> /** * AssetCell is the base asset cell component which takes a generic asset objects and displays the corresponding cell * e.g. DocumentCell, ImageCell, UnknownFileCell, VideoCell * NOTE: AssetCell is wrapped as a react-dnd dragSource, use AssetCellStandard for custom drag'n'drop implementations */ @connect(({ imageRequestQueue }) => ({ imageRequestQueueEnabled: imageRequestQueue.enabled })) export class AssetCellStandard extends Component { static propTypes = { /** Tank asset object */ asset: PropTypes.object.isRequired, /** If asset cell is selected */ selected: PropTypes.bool, /** React component for a custom cell toolbar */ Toolbar: PropTypes.any, /** Sets maxWidth of cell (cell expands with flex) */ cellSize: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), /** Tank asset size string, e.g. cockpit-150 */ size: PropTypes.string, /** custom class name for cell */ className: PropTypes.string, onClick: PropTypes.func, onDoubleClick: PropTypes.func, onEdit: PropTypes.func, onDetails: PropTypes.func, onRemove: PropTypes.func, } static defaultProps = { selected: false, Toolbar: DefaultAssetCellToolbar, cellSize: 150, size: 'cockpit-150', className: null, onClick: () => console.warn('[AssetCell] no onClick prop'), onDoubleClick: () => console.warn('[AssetCell] no onDoubleClick prop'), onEdit: () => console.warn('[AssetCell] no onEdit prop'), onPlay: () => console.warn('[AssetCell] no onPlay prop'), onDetails: () => console.warn('[AssetCell] no onDetails prop'), onRemove: () => console.warn('[AssetCell] no onRemove prop'), } constructor(props) { super(props) this.handleClick = _throttle(this.handleClick.bind(this), 100) this.state = { broken: false, generatedAssetUrl: null, generatedAssetUrlWaiting: props.imageRequestQueueEnabled } } componentDidMount() { this.requestCurrentGeneratedImage() } componentDidUpdate(prevProps) { if (this.props.imageRequestQueueEnabled && this.props.asset && this.props.asset.kind === 'image') { if ((this.props.asset && this.props.asset.id) !== (prevProps.asset && prevProps.asset.id)) { this.setState({ generatedAssetUrl: null, generatedAssetUrlWaiting: !!this.props.asset }) this.cancelCurrentGeneratedImageRequest(prevProps.asset, prevProps.size) this.requestCurrentGeneratedImage() } } } componentWillUnmount() { this.cancelCurrentGeneratedImageRequest() } requestCurrentGeneratedImage() { if (!this.props.imageRequestQueueEnabled) return if (!this.props.asset || this.props.asset.kind !== 'image') return this.props.dispatch(requestGeneratedImage(this.props.asset, this.props.size, this.handleGeneratedAssetUrlReceived)) } cancelCurrentGeneratedImageRequest(asset = this.props.asset, size = this.props.size) { if (!this.props.imageRequestQueueEnabled) return if (!asset || asset.kind !== 'image') return if (this.state.generatedAssetUrlWaiting && !this.state.generatedAssetUrl) { this.props.dispatch(cancelGeneratedImageRequest(asset, size, this.handleGeneratedAssetUrlReceived)) } } handleGeneratedAssetUrlReceived = url => { this.setState({ generatedAssetUrl: url, generatedAssetUrlWaiting: false }) } handleImageStatus = (broken = false) => { this.setState({ broken, generatedAssetUrlWaiting: false }) if (this.props.imageRequestQueueEnabled) { this.props.dispatch( generatedImageRequestFulfilled(this.props.asset, this.props.size, this.handleGeneratedAssetUrlReceived) ) } } handleClick(event) { this.props.onClick() } render() { const { imageRequestQueueEnabled, connectDragSource, isDragSource, isDragging, asset, className, size, cellSize, selected, onClick, onDoubleClick, Toolbar, ...rest } = this.props let assetUrl = getAssetUrl(asset) let AssetComponent = null let queued = false const toolbarProps = { onRemove: this.props.onRemove, onDetails: this.props.onDetails, } switch (asset.kind) { case 'image': AssetComponent = ImageCell assetUrl = imageRequestQueueEnabled ? this.state.generatedAssetUrl : getImageUrl(asset, size) queued = imageRequestQueueEnabled && !this.state.generatedAssetUrl && !this.state.broken toolbarProps.onEdit = this.props.onEdit break case 'video': AssetComponent = VideoCell toolbarProps.onPlay = this.props.onPlay break case 'audio': AssetComponent = AudioCell toolbarProps.onPlay = this.props.onPlay break case 'pdf': case 'excel': case 'word': case 'text': case 'html': AssetComponent = DocumentCell break default: AssetComponent = UnknownFileCell } return ( <div className={classnames('image', className, { selected: selected, dragging: isDragging })} onClick={this.handleClick} onDoubleClick={onDoubleClick} style={{ maxWidth: cellSize }}> <div className="image-inner"> <AssetComponent {...asset} size={size} url={assetUrl} onStatus={this.handleImageStatus} queued={queued} {...rest} /> {Toolbar && ( <Toolbar asset={asset} isDragging={isDragging} queued={queued} broken={this.state.broken} {...toolbarProps} /> )} </div> </div> ) } } const AssetCell = DragSource(props => DragTypes.ASSET_CELL, dragSource, (connect, monitor) => ({ connectDragSource: connect.dragSource(), isDragging: monitor.isDragging(), isDragSource: true, }))(({ connectDragSource, ...props }) => connectDragSource( <div> <AssetCellStandard {...props} /> </div> ) ) export default AssetCell