UNPKG

labo-components

Version:
246 lines (215 loc) 7.76 kB
import React from 'react'; import PropTypes from 'prop-types'; import classNames from 'classnames'; import IDUtil from '../../../util/IDUtil'; import { SELECTION_TEMPORAL, SELECTION_SPATIAL } from '../../../util/AnnotationConstants'; import { MSAnnotationUtil } from '../AnnotationClient'; import { getSegmentTitle } from '../AnnotationHelpers'; import { ResourceViewerContext } from '../ResourceViewerContext'; import TimeInput from './TimeInput'; import Strings from '../_Strings'; export default class SegmentHeader extends React.PureComponent { static contextType = ResourceViewerContext; constructor(props) { super(props); this.state = { edit: false, }; this.editSelection = null; } componentDidUpdate() { // stop edit mode when not highlighted if (!this.props.highlight && this.state.edit) { this.stopEdit(); } } // INLINE EDITING // return an update function for editing a temporal selection updateTemporalEditSelection = (field) => { return (value) => { this.editSelection[field] = value; }; }; // return an update function for editing a spatial selection updateSpatialEditSelection = (field) => { return (e) => { this.editSelection.rect[field] = e.target.value; }; }; // return a save/setter function for the saveSelection = (segment) => { return () => { const selection = Object.assign({}, this.editSelection); this.props.updateSegment(segment, selection); this.stopEdit(); }; }; // delete segment deleteSegment = (segment) => { return (e) => { e.stopPropagation(); if (!confirm(Strings.SEGMENT_DELETE_CONFIRM)) { return; } // sending null to the update function results in the segment getting deleted this.props.updateSegment(segment, null); this.stopEdit(); }; }; onTitleClick = (e) => { e.stopPropagation(); if (!this.props.highlight) { this.props.onSelect(this.props.segment); } }; onTitleDoubleClick = (e) => { e.stopPropagation(); this.setState({ edit: true }); }; stopEdit = () => { this.setState({ edit: false }); }; stopEvent = (e) => { e.stopPropagation(); }; // Render form for inline editing renderForm = (segment) => { // Check for valid selection const selection = MSAnnotationUtil.extractSelectionFromTarget( segment.target ); if (!selection) { console.warn("Could not find selection for target", segment.target); return null; } // store selection that is being edited this.editSelection = selection; switch (selection.type) { case SELECTION_TEMPORAL: return ( <div className="form temporal" onClick={this.stopEvent} title={Strings.SEGMENT_TITLE_EDIT_SUBMIT} > <TimeInput time={selection.start} onUpdate={this.updateTemporalEditSelection("start")} onFinish={this.saveSelection(segment)} /> {" - "} <TimeInput time={selection.end} onUpdate={this.updateTemporalEditSelection("end")} onFinish={this.saveSelection(segment)} /> </div> ); case SELECTION_SPATIAL: const inputs = ["x", "y", "w", "h"].map((v) => ( <span key={v}> <input type="number" ref={this[v]} defaultValue={selection.rect[v]} onFocus={(e) => { e.target.select(); }} onChange={this.updateSpatialEditSelection(v)} onKeyDown={(e) => { if (e.keyCode == 13) { this.saveSelection(segment)(); } }} /> </span> )); return ( <div className="form spatial" onClick={this.stopEvent} title={Strings.SEGMENT_TITLE_EDIT_SUBMIT} > {inputs} </div> ); default: return null; } }; playSegment = (startTime) => { if (startTime && this.context.playerAPI) { const resourceStart = this.context.activeMediaObject && this.context.activeMediaObject.resourceStart ? this.context.activeMediaObject.resourceStart + startTime : startTime; this.context.playerAPI.seek(resourceStart); } }; render() { // Delete button const deleteButton = ( <span className="delete" title={Strings.SEGMENT_DELETE_TITLE} onClick={this.deleteSegment(this.props.segment)} /> ); // content is a titletext or an editable form const content = this.state.edit ? ( // edit mode this.renderForm(this.props.segment) ) : ( // view mode <span className="title" title={Strings.SEGMENT_TITLE_EDIT} onClick={this.onTitleClick} onDoubleClick={this.onTitleDoubleClick} > {getSegmentTitle(this.props.segment, this.props.activeTypes)} </span> ); const startTime = this.props.segment.target.selector && this.props.segment.target.selector.refinedBy ? this.props.segment.target.selector.refinedBy.start : null; return ( <div className={classNames(IDUtil.cssClassName("segment-header"), { active: this.props.active, highlight: this.props.highlight, })} onClick={this.props.onToggle} > <span className="play-segment" onClick={(e) => { this.playSegment(startTime); e.stopPropagation(); }} > <i className="fas fa-play" title="Play segment" /> </span> <span className="left">{content}</span> <span className="count"> {this.props.segment.body ? this.props.segment.body.length : 0} </span> {deleteButton} </div> ); } } SegmentHeader.propTypes = { segment: PropTypes.object.isRequired, active: PropTypes.bool.isRequired, highlight: PropTypes.bool.isRequired, onToggle: PropTypes.func.isRequired, onSelect: PropTypes.func.isRequired, updateSegment: PropTypes.func.isRequired, activeTypes: PropTypes.array.isRequired, };