nexshop-web-contents
Version:
Nexshop Web Contents Project
285 lines (253 loc) • 13.1 kB
JavaScript
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {DropDown} from 'nexshop-web-elements';
import {formatSeconds} from "../../../helpers/time-helper";
import {leftPadZero} from "../../../helpers/string-helper";
import {EFFECT_TYPES} from "../../../constants/effect-types"
import {DURATION_ITEMS} from "../../../constants/duration-items";
import {FIT_MODES} from "../../../constants/fit-modes"
class PlaylistEditorView extends Component {
constructor(props) {
super(props);
this.state = {
mouseOverUUID: '',
mouseOverDomType: '',
isDragging: false,
top: 1,
left: 1,
width: 1,
draggingContentItem: null,
selectItem: {
uuid: '',
selectedValue: '',
}
};
this.onMouseOver = this.onMouseOver.bind(this);
this.onMouseLeave = this.onMouseLeave.bind(this);
this.onDeleteClick = this.onDeleteClick.bind(this);
this.onDragStart = this.onDragStart.bind(this);
this.onDragEnd = this.onDragEnd.bind(this);
this.onDragOver = this.onDragOver.bind(this);
this.onTransitionEffectChange = this.onTransitionEffectChange.bind(this);
this.onDurationChange = this.onDurationChange.bind(this);
this.onFitModeChange = this.onFitModeChange.bind(this);
}
onMouseOver(uuid) {
return () => {
if (this.state.isDragging) return;
this.setState({
mouseOverUUID: uuid
});
}
}
onMouseLeave() {
this.setState({
mouseOverUUID: ''
});
}
onDeleteClick(index) {
const {onDeleteItem} = this.props;
onDeleteItem(index);
setTimeout(() => {
//this will be triggered after view is rendered
const {contentItems} = this.props;
let mouseOverUUID = '';
if (index < contentItems.length) {
mouseOverUUID = contentItems[index].uuid
}
this.setState({mouseOverUUID});
}, 0)
}
onDragStart(index) {
const {onOrderChangeStart, contentItems} = this.props;
return (e) => {
const {currentTarget, nativeEvent: {offsetX, offsetY}} = e;
this.setState({isDragging: true, draggingContentItem: {...contentItems[index]}});
//TODO: not tested
this.preview.style.width = `${currentTarget.clientWidth + 5}px`;
//TODO: technical dept
e.dataTransfer.effectAllowed = 'copy';
e.dataTransfer.setDragImage(this.preview, offsetX, offsetY);
onOrderChangeStart(index);
}
}
onDragEnd() {
const {onOrderChangeEnd} = this.props;
this.setState({isDragging: false, draggingContentItem: null});
onOrderChangeEnd();
}
onDragOver(index) {
const {onOrderChangeProcess} = this.props;
return (e) => {
e.preventDefault();
onOrderChangeProcess(index);
}
}
onTransitionEffectChange(uuid) {
const {onUpdateContentItemTransition} = this.props;
return ({value}) => {
onUpdateContentItemTransition(uuid, value);
};
}
onDurationChange(uuid) {
const {onUpdateContentItemDuration} = this.props;
return ({value}) => {
onUpdateContentItemDuration(uuid, value);
};
}
onFitModeChange(uuid) {
const {onUpdateContentItemFitMode} = this.props;
return ({value}) => {
onUpdateContentItemFitMode(uuid, value);
};
}
render() {
const {
resolution: {value, orientation},
onDropDragContentsItem,
contentItems,
blankIndex,
onOrderChangeEnd,
onSelectPreviewItem,
} = this.props;
let hasContent = (contentItems && contentItems.length > 0);
return (
<div className="playlist-editor-wrapper" onDragOver={(e) => e.preventDefault()}
onDrop={onDropDragContentsItem}>
<div className="playlist-editor">
<div className="playlist-top">
<div className="playlist-summary">
<span className="playlist-summary__label">Counts</span>
<span className="playlist-summary__value">{leftPadZero(contentItems.length)}</span>
<span className="playlist-summary__label">Total</span>
<span className="playlist-summary__value">{
formatSeconds(contentItems.reduce((sum, content) => sum + content.durationSeconds, 0),'hh:mm:ss')
}</span>
</div>
<div className="playlist-resolution">
<div className="playlist-resolution__value">{value}</div>
<div className={`playlist-resolution__${orientation}`}/>
</div>
</div>
<div className="playlist-items">
{
hasContent &&
contentItems.map(({contents, uuid, transitionEffect, durationSeconds, fitMode}, index) => {
return (
index === blankIndex ?
<div key={uuid} className="playlist-placeholder" onDragEnd={this.onDragEnd}
onDrop={onOrderChangeEnd}/>
:
<div key={uuid} data-uuid={uuid}
className={`playlist-item${this.state.mouseOverUUID === uuid ? '--hovered' : ''}`}
onMouseLeave={this.onMouseLeave}
onMouseOver={this.onMouseOver(uuid)}
onDragStart={this.onDragStart(index)}
onDragEnd={this.onDragEnd}
onDragOver={this.onDragOver(index)}
draggable={true}>
{uuid === this.state.mouseOverUUID &&
<div className="playlist-item-preview-link">
<div className="playlist-item-preview" onClick={() => {
onSelectPreviewItem(index)
}}>
<div className="playlist-item-preview__play"/>
</div>
</div>}
<div className="playlist-item__thumbnail"
style={{backgroundImage: `url(${contents.thumbnailUrls.thumbnail ? contents.thumbnailUrls.thumbnail : ''})`}}/>
<div className={`playlist-item__type--${contents.type}`}/>
<div className="playlist-item__name">{contents.name}</div>
<div
className={`playlist-item__duration${contents.type === 'video' ? '__video' : ''}`}>
{
contents.type === 'video' ?
(
formatSeconds(durationSeconds, durationSeconds >= 3600 ? 'hh:mm:ss' : 'mm:ss')
) :
(
<DropDown items={DURATION_ITEMS}
selected={DURATION_ITEMS.find((item) => item.value === durationSeconds)}
onSelect={this.onDurationChange(uuid)}/>
)
}
</div>
<div className='playlist-item__effect'>
{
contents.type === 'video' ?
(
<div>No Effect</div>
) :
(
<DropDown items={EFFECT_TYPES}
selected={EFFECT_TYPES.find((item) => item.value === transitionEffect)}
onSelect={this.onTransitionEffectChange(uuid)}/>
)
}
</div>
<div className="playlist-item__fitMode">
<DropDown items={FIT_MODES}
selected={FIT_MODES.find((item) => item.value === fitMode)}
onSelect={this.onFitModeChange(uuid)}
classNamePrefix={'playlist-fitMode'}
/>
</div>
{
this.state.mouseOverUUID === uuid &&
<div className="playlist-item__delete"
onClick={() => this.onDeleteClick(index)}/>
}
</div>
)
})
}
{
!hasContent &&
<div className="playlist-item-drag-placeholder">
Drag & drop contents
</div>
}
</div>
</div>
<div className="dragging" ref={(preview) => this.preview = preview}>
{
this.state.draggingContentItem &&
<DraggingContentItem contentItem={this.state.draggingContentItem}
durationSeconds={this.state.draggingContentItem.durationSeconds}
transitionEffect={EFFECT_TYPES.filter((item) =>
item.value === this.state.draggingContentItem.transitionEffect)[0]}
/>
}
</div>
</div>
);
}
}
const DraggingContentItem = ({contentItem: {contents}, transitionEffect, durationSeconds}) => {
let formattedDurationSeconds = formatSeconds(durationSeconds, durationSeconds >= 3600 ? 'hh:mm:ss' : 'mm:ss');
return (
<div className='playlist-item playlist-item-shadow'>
<div className="playlist-item__thumbnail"
style={{backgroundImage: `url(${contents.thumbnailUrls.thumbnail ? contents.thumbnailUrls.thumbnail : ''})`}}/>
<div className={`playlist-item__type--${contents.type}`}/>
<div className="playlist-item__name">{contents.name}</div>
<div className="playlist-item__duration">{formattedDurationSeconds}</div>
<div className="playlist-item__effect">{transitionEffect.text}</div>
</div>
)
};
PlaylistEditorView.propTypes = {
resolution: PropTypes.object.isRequired,
contentItems: PropTypes.array.isRequired,
onDropDragContentsItem: PropTypes.func.isRequired,
onDeleteItem: PropTypes.func.isRequired,
onOrderChangeStart: PropTypes.func.isRequired,
onOrderChangeProcess: PropTypes.func.isRequired,
onOrderChangeEnd: PropTypes.func.isRequired,
blankIndex: PropTypes.number,
onSelectPreviewItem: PropTypes.func.isRequired,
onUpdateContentItemTransition: PropTypes.func.isRequired,
onUpdateContentItemDuration: PropTypes.func.isRequired,
onUpdateContentItemFitMode: PropTypes.func.isRequired,
};
export default PlaylistEditorView;