Skip to content

Commit

Permalink
Added segment 'markers' and 'overlay' options
Browse files Browse the repository at this point in the history
This allows individual segments to be created as
marker-style or overlay-style.

This commit also adds a click event handler to
marker handles, to bring the segment to the
top of the z-order, so it can be dragged.

See #544
  • Loading branch information
chrisn committed Sep 8, 2024
1 parent e994f9a commit 34fec6b
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 53 deletions.
12 changes: 6 additions & 6 deletions doc/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -601,9 +601,7 @@ The top level `Peaks` object exposes a factory function to create new `Peaks` in

### `Peaks.init(options, callback)`

Creates a new `Peaks` instance with the [assigned options](#Configuration).
The callback is invoked after the instance has been created and initialized, or if any errors occur during initialization.
You can create and manage several `Peaks` instances within a single page with one or several configurations.
Creates a new `Peaks` instance with the given [options](#Configuration). The callback is invoked after the instance has been created and initialized, or if any errors occur during initialization. You can create and manage several `Peaks` instances within a single page, each with its own configuration.

```js
const options = { ... };
Expand Down Expand Up @@ -1233,9 +1231,11 @@ Adds a segment to the waveform timeline. Accepts an object containing the follow
* `startTime`: the segment start time (seconds)
* `endTime`: the segment end time (seconds)
* `editable`: (optional) sets whether the segment is user editable (boolean, defaults to `false`)
* `color`: (optional) the segment color. If not specified, the segment is given a default color (set by the [`segmentoptions.waveformColor` option](#Configuration) for marker-style segments or the [`segmentOptions.overlayColor` option](#Configuration) for overlay-style segments)
* `borderColor`: (optional) the segment border color. This applies only to overlay style segments. If not specified, the segment is given a default border color (set by the [`segmentOptions.overlayBorderColor` option](#Configuration))
* `labelText`: (option) a text label which is displayed when the user hovers the mouse pointer over the segment
* `color`: (optional) the segment color. If not specified, the segment is given a default color set by the [`segmentOptions.overlayColor`](#configuration) option for segments with overlays, or [`segmentoptions.waveformColor`](#configuration) option for segments without overlays
* `borderColor`: (optional) the segment border color. This applies only to segments with overlays. If not specified, the segment is given a default border color (set by the [`segmentOptions.overlayBorderColor` option](#configuration))
* `markers`: (optional) sets whether the segment has markers. If not specified, the default is set by the [`segmentOptions.markers`](#configuration) option
* `overlay`: (optional) sets whether the segment has an overlay. If not specified, the default is set by the [`segmentOptions.overlay`](#configuration) option
* `labelText`: (optional) a text label which is displayed when the user hovers the mouse pointer over the segment
* `id`: (optional) the segment identifier. If not specified, the segment is automatically given a unique identifier

```js
Expand Down
21 changes: 13 additions & 8 deletions src/segment-marker.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,16 @@ import Konva from 'konva/lib/Core';
function SegmentMarker(options) {
const self = this;

self._segment = options.segment;
self._marker = options.marker;
self._segmentShape = options.segmentShape;
self._editable = options.editable;
self._startMarker = options.startMarker;
self._segment = options.segment;
self._marker = options.marker;
self._segmentShape = options.segmentShape;
self._editable = options.editable;
self._startMarker = options.startMarker;

self._onDragStart = options.onDragStart;
self._onDragMove = options.onDragMove;
self._onDragEnd = options.onDragEnd;
self._onClick = options.onClick;
self._onDragStart = options.onDragStart;
self._onDragMove = options.onDragMove;
self._onDragEnd = options.onDragEnd;

self._group = new Konva.Group({
name: 'segment-marker',
Expand All @@ -65,6 +66,10 @@ function SegmentMarker(options) {
SegmentMarker.prototype._bindDefaultEventHandlers = function() {
const self = this;

self._group.on('click', function(event) {
self._onClick(self, event);
});

self._group.on('dragstart', function(event) {
self._onDragStart(self, event);
});
Expand Down
91 changes: 53 additions & 38 deletions src/segment-shape.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,25 +71,26 @@ function SegmentShape(segment, peaks, layer, view) {

this._overlayOffset = segmentOptions.overlayOffset;

if (!segmentOptions.overlay) {
if (!segment.overlay) {
this._waveformShape = new WaveformShape({
color: segment.color,
view: view,
segment: segment
});
}

this._onMouseEnter = this._onMouseEnter.bind(this);
this._onMouseLeave = this._onMouseLeave.bind(this);
this._onMouseDown = this._onMouseDown.bind(this);
this._onMouseUp = this._onMouseUp.bind(this);
this._onMouseEnter = this._onMouseEnter.bind(this);
this._onMouseLeave = this._onMouseLeave.bind(this);
this._onMouseDown = this._onMouseDown.bind(this);
this._onMouseUp = this._onMouseUp.bind(this);

this._dragBoundFunc = this._dragBoundFunc.bind(this);
this._onSegmentDragStart = this._onSegmentDragStart.bind(this);
this._onSegmentDragMove = this._onSegmentDragMove.bind(this);
this._onSegmentDragEnd = this._onSegmentDragEnd.bind(this);

// Event handlers for markers
this._onSegmentMarkerClick = this._onSegmentMarkerClick.bind(this);
this._onSegmentMarkerDragStart = this._onSegmentMarkerDragStart.bind(this);
this._onSegmentMarkerDragMove = this._onSegmentMarkerDragMove.bind(this);
this._onSegmentMarkerDragEnd = this._onSegmentMarkerDragEnd.bind(this);
Expand Down Expand Up @@ -134,7 +135,7 @@ function SegmentShape(segment, peaks, layer, view) {

let overlayBorderColor, overlayBorderWidth, overlayColor, overlayOpacity, overlayCornerRadius;

if (segmentOptions.overlay) {
if (segment.overlay) {
overlayBorderColor = this._borderColor || segmentOptions.overlayBorderColor;
overlayBorderWidth = segmentOptions.overlayBorderWidth;
overlayColor = this._color || segmentOptions.overlayColor;
Expand All @@ -156,7 +157,7 @@ function SegmentShape(segment, peaks, layer, view) {

this._overlay.add(this._overlayRect);

if (segmentOptions.overlay) {
if (segment.overlay) {
this._overlayText = new Konva.Text({
x: 0,
y: this._overlayOffset,
Expand Down Expand Up @@ -201,22 +202,29 @@ SegmentShape.prototype._createMarkers = function() {
const editable = this._layer.isEditingEnabled() && this._segment.editable;
const segmentOptions = this._view.getViewOptions().segmentOptions;

const createSegmentMarker = segmentOptions.markers ?
this._peaks.options.createSegmentMarker :
createOverlayMarker;

const startMarker = createSegmentMarker({
segment: this._segment,
editable: editable,
startMarker: true,
color: segmentOptions.startMarkerColor,
fontFamily: this._peaks.options.fontFamily || defaultFontFamily,
fontSize: this._peaks.options.fontSize || defaultFontSize,
fontStyle: this._peaks.options.fontStyle || defaultFontShape,
layer: this._layer,
view: this._view.getName(),
segmentOptions: this._view.getViewOptions().segmentOptions
});
let createSegmentMarker, startMarker, endMarker;

if (this._segment.markers) {
createSegmentMarker = this._peaks.options.createSegmentMarker;
}
else if (this._segment.overlay) {
createSegmentMarker = createOverlayMarker;
}

if (createSegmentMarker) {
startMarker = createSegmentMarker({
segment: this._segment,
editable: editable,
startMarker: true,
color: segmentOptions.startMarkerColor,
fontFamily: this._peaks.options.fontFamily || defaultFontFamily,
fontSize: this._peaks.options.fontSize || defaultFontSize,
fontStyle: this._peaks.options.fontStyle || defaultFontShape,
layer: this._layer,
view: this._view.getName(),
segmentOptions: this._view.getViewOptions().segmentOptions
});
}

if (startMarker) {
this._startMarker = new SegmentMarker({
Expand All @@ -225,25 +233,28 @@ SegmentShape.prototype._createMarkers = function() {
editable: editable,
startMarker: true,
marker: startMarker,
onClick: this._onSegmentMarkerClick,
onDragStart: this._onSegmentMarkerDragStart,
onDragMove: this._onSegmentMarkerDragMove,
onDragEnd: this._onSegmentMarkerDragEnd,
dragBoundFunc: this._segmentMarkerDragBoundFunc
});
}

const endMarker = createSegmentMarker({
segment: this._segment,
editable: editable,
startMarker: false,
color: segmentOptions.endMarkerColor,
fontFamily: this._peaks.options.fontFamily || defaultFontFamily,
fontSize: this._peaks.options.fontSize || defaultFontSize,
fontStyle: this._peaks.options.fontStyle || defaultFontShape,
layer: this._layer,
view: this._view.getName(),
segmentOptions: this._view.getViewOptions().segmentOptions
});
if (createSegmentMarker) {
endMarker = createSegmentMarker({
segment: this._segment,
editable: editable,
startMarker: false,
color: segmentOptions.endMarkerColor,
fontFamily: this._peaks.options.fontFamily || defaultFontFamily,
fontSize: this._peaks.options.fontSize || defaultFontSize,
fontStyle: this._peaks.options.fontStyle || defaultFontShape,
layer: this._layer,
view: this._view.getName(),
segmentOptions: this._view.getViewOptions().segmentOptions
});
}

if (endMarker) {
this._endMarker = new SegmentMarker({
Expand All @@ -252,6 +263,7 @@ SegmentShape.prototype._createMarkers = function() {
editable: editable,
startMarker: false,
marker: endMarker,
onClick: this._onSegmentMarkerClick,
onDragStart: this._onSegmentMarkerDragStart,
onDragMove: this._onSegmentMarkerDragMove,
onDragEnd: this._onSegmentMarkerDragEnd,
Expand Down Expand Up @@ -301,9 +313,7 @@ SegmentShape.prototype.update = function(options) {
this._overlayText.text(this._segment.labelText);
}

const segmentOptions = this._view.getViewOptions().segmentOptions;

if (segmentOptions.overlay) {
if (this._segment.overlay) {
if (this._color) {
this._overlayRect.fill(this._color);
}
Expand Down Expand Up @@ -941,6 +951,11 @@ SegmentShape.prototype._segmentMarkerDragBoundFunc = function(segmentMarker, pos
};
};

SegmentShape.prototype._onSegmentMarkerClick = function() {
// Move this segment to the top of the z-order.
this._moveToTop();
};

SegmentShape.prototype.fitToView = function() {
if (this._startMarker) {
this._startMarker.fitToView();
Expand Down
41 changes: 40 additions & 1 deletion src/segment.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import {
} from './utils';

const segmentOptions = [
'id', 'pid', 'startTime', 'endTime', 'labelText', 'color', 'borderColor', 'editable'
'id', 'pid', 'startTime', 'endTime', 'labelText', 'color', 'borderColor',
'markers', 'overlay', 'editable'
];

const invalidOptions = [
Expand All @@ -37,6 +38,14 @@ function setDefaultSegmentOptions(options, globalSegmentOptions) {
options.labelText = '';
}

if (isNullOrUndefined(options.markers)) {
options.markers = globalSegmentOptions.markers;
}

if (isNullOrUndefined(options.overlay)) {
options.overlay = globalSegmentOptions.overlay;
}

if (isNullOrUndefined(options.editable)) {
options.editable = false;
}
Expand Down Expand Up @@ -80,6 +89,22 @@ function validateSegmentOptions(options, updating) {
throw new TypeError('peaks.segments.' + context + ': labelText must be a string');
}

if (updating && objectHasProperty(options, 'markers')) {
throw new TypeError('peaks.segments.' + context + ': cannot update markers attribute');
}

if (objectHasProperty(options, 'markers') && !isBoolean(options.markers)) {
throw new TypeError('peaks.segments.' + context + ': markers must be true or false');
}

if (updating && objectHasProperty(options, 'overlay')) {
throw new TypeError('peaks.segments.' + context + ': cannot update overlay attribute');
}

if (objectHasProperty(options, 'overlay') && !isBoolean(options.overlay)) {
throw new TypeError('peaks.segments.' + context + ': overlay must be true or false');
}

if (objectHasProperty(options, 'editable') && !isBoolean(options.editable)) {
throw new TypeError('peaks.segments.' + context + ': editable must be true or false');
}
Expand Down Expand Up @@ -130,6 +155,8 @@ function Segment(peaks, pid, options) {
this._color = options.color;
this._borderColor = options.borderColor;
this._editable = options.editable;
this._markers = options.markers;
this._overlay = options.overlay;

this._setUserData(options);
}
Expand Down Expand Up @@ -190,6 +217,18 @@ Object.defineProperties(Segment.prototype, {
return this._borderColor;
}
},
markers: {
enumerable: true,
get: function() {
return this._markers;
}
},
overlay: {
enumerable: true,
get: function() {
return this._overlay;
}
},
editable: {
enumerable: true,
get: function() {
Expand Down
22 changes: 22 additions & 0 deletions test/segment-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,26 @@ describe('Segment', function() {
}).to.throw(Error);
});

it('should not allow the overlay attribute to be updated', function() {
p.segments.add({ startTime: 0, endTime: 10, labelText: 'test' });

const segment = p.segments.getSegments()[0];

expect(function() {
segment.update({ overlay: false });
}).to.throw(TypeError);
});

it('should not allow the markers attribute to be updated', function() {
p.segments.add({ startTime: 0, endTime: 10, labelText: 'test' });

const segment = p.segments.getSegments()[0];

expect(function() {
segment.update({ markers: false });
}).to.throw(TypeError);
});

it('should allow a user data attribute to be created', function() {
const peaks = { emit: function() {} };
const pid = 0;
Expand Down Expand Up @@ -246,6 +266,8 @@ describe('Segment', function() {
'_labelText',
'_color',
'_borderColor',
'_overlay',
'_markers',
'_editable'
].forEach(function(name) {
it('should not allow an invalid user data attribute name: ' + name, function() {
Expand Down

0 comments on commit 34fec6b

Please sign in to comment.