labo-components
Version:
179 lines (161 loc) • 6.62 kB
JSX
import React from "react";
import PropTypes from "prop-types";
import classNames from "classnames";
const LAYER_HEADER_WIDTH = 210;
import IDUtil from "../../../../util/IDUtil";
import Section from "./Section";
// Render the given layers with a slight delay, so the UI has time to process the mouse interaction
class Layer extends React.Component {
constructor(props) {
super(props);
this.doubleClickTime = this.props.doubleClickTime
? this.props.doubleClickTime
: 200;
this.doubleClickOffset = this.props.doubleClickOffset
? this.props.doubleClickOffset
: 5;
}
onMouseDown = (e) => {
const leftMouseButton = e.button === 0;
const time = new Date().getTime();
const clickPosition =
this.props.start +
(e.pageX - LAYER_HEADER_WIDTH) / this.props.pixelsPerSecond;
// check for double click and if available call layer.onDoubleClick
if (
leftMouseButton &&
this.interactionVars &&
this.props.layer.onDoubleClick &&
time - this.interactionVars.lastDown < this.doubleClickTime &&
e.pageX - this.interactionVars.downX < this.doubleClickOffset
) {
this.interactionVars = null;
e.stopPropagation();
// pass to onDoubleClick
e.persist();
this.props.layer.onDoubleClick(clickPosition, e);
} else {
// set interaction variables
this.interactionVars = {
downX: e.pageX,
lastDown: leftMouseButton ? time : 0,
};
// make layer active
this.props.onSectionClick(this.props.layer.id, "");
// optional onMouseDown callback
this.props.layer.onClick &&
this.props.layer.onClick(clickPosition, e.originalEvent);
}
};
onSectionClick = (layerId, sectionId) => {
this.props.onSectionClick(layerId, sectionId);
};
render() {
const {
layer,
start,
end,
pixelsPerSecond,
activeLayerId,
activeSectionId,
} = this.props;
// Get layers with sections in view
const durationPadding = (end - start) * 0.03;
return (
<div
className={classNames(
IDUtil.cssClassName("tl-layer"),
layer.className
)}
style={{
height: layer.height,
}}
onMouseDown={this.onMouseDown}
layer={layer.id}
>
{layer.sections
.filter(
(section) =>
section.start >
start -
(section.end - section.start) -
durationPadding &&
section.end <
end +
(section.end - section.start) +
durationPadding
)
.map((section) => (
<Section
key={section.id}
sectionId={section.id}
layerId={layer.id}
start={section.start}
active={
layer.id === activeLayerId &&
section.id === activeSectionId
}
cropped={section.start < start}
highlight={section.highlight}
match={section.match}
left={Math.round(
section.start >= start
? section.start * pixelsPerSecond
: start * pixelsPerSecond
)}
width={
Math.round(
section.start >= start
? Math.max(
0.25, // always keep a minimum duration in view so the section could still be edited
section.end - section.start
) * pixelsPerSecond
: (Math.max(
0.25, // always keep a minimum duration in view so the section could still be edited
section.end - section.start
) +
Math.min(
0,
section.start - start
)) *
pixelsPerSecond
) + 1 // +1 for filling gaps
}
onClick={this.onSectionClick}
>
{section.data}
</Section>
))}
</div>
);
}
}
export const LayerPropTypes = PropTypes.shape({
id: PropTypes.number.isRequired,
title: PropTypes.oneOfType([PropTypes.string, PropTypes.object]).isRequired,
description: PropTypes.string,
sections: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.string.isRequired,
start: PropTypes.number.isRequired,
end: PropTypes.number.isRequired,
rawData: PropTypes.string,
data: PropTypes.object.isRequired,
}).isRequired
).isRequired,
onClick: PropTypes.func, // on layer click
onDoubleClick: PropTypes.func, // on layer double click
headerChildren: PropTypes.object, // additional children to render in layer header
}).isRequired;
Layer.propTypes = {
start: PropTypes.number.isRequired,
end: PropTypes.number.isRequired,
pixelsPerSecond: PropTypes.number.isRequired,
layer: LayerPropTypes,
activeLayerId: PropTypes.number,
activeSectionId: PropTypes.string,
onSectionClick: PropTypes.func,
doubleClickTime: PropTypes.number,
doubleClickOffset: PropTypes.number,
};
export default Layer;