'use strict'; /** * Licensed Materials - Property of IBM * IBM Cognos Products: Storytelling * (C) Copyright IBM Corp. 2017, 2020 * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp. */ define(['baglass/core-client/js/core-client/ui/core/View', 'jquery', 'gemini/app/util/ScreenReaderUtil', 'storytelling-ui/storytelling-ui.min', 'text!./templates/TimelineHighlightIndicatorView.html', 'dashboard-analytics/visualizations/renderer/filter/FilterLabelHelper', './highlight/TimelineHighlightFlyoutController', 'react', 'react-dom', '../util/WidgetHelper'], function (View, $, ScreenReaderUtil, StorytellingUI, Template, FilterLabelHelper, TimelineHighlightFlyoutController, React, ReactDOM, WidgetHelper) { var Indicator = View.extend({ templateString: Template, //@override /** * @param options.$el {object} - JQuery node to append the highlight HTML * @param options.widgetId {string} - the widget Id of the selected timeline widget * @param options.scaleManager {object} - scaleManager of the timeline * @param options.episodeModel {object} - episodeModel of the selected timeline widget * @param options.highlightAct {object} - optional, the existing highlightAct model */ init: function init(options) { Indicator.inherited('init', this, arguments); this.controller = options.controller; this.widgetId = options.widgetId; this.scaleManager = options.scaleManager; this.episodeModel = options.episodeModel; this.highlightAct = options.highlightAct; this.id = options.highlightAct.id; this.services = options.services; this.dashboardApi = options.dashboardApi; this.widgetHelper = new WidgetHelper({ dashboardApi: this.dashboardApi }); this.widget = this.widgetHelper.getWidget(this.widgetId); this.content = this.widgetHelper.getContent(this.widgetId); if (this.widget && this.content) { this.visualization = this.content.getFeature('Visualization'); this.filterLabelHelper = new FilterLabelHelper({ dataSource: this.visualization.getDataSource(), dashboardApi: this.dashboardApi }); } this.pageContextAPI = this.controller.model.pageContext.getAPI(); this.extraCssClass = options.extraCssClass || ''; this._ScreenReader = new ScreenReaderUtil(); this.stringResources = this.dashboardApi.getDashboardCoreSvc('.StringResources'); this.flyoutHolder = document.createElement('div'); this.flyoutHolder.classList.add('flyout-holder'); }, render: function render() { var label = this._getTooltipLabel(); this.el.id = 'timelineHighlight_' + this.id; this.el.setAttribute('role', 'application'); this.el.className = 'timelineHighlight ' + this.extraCssClass; this.el.setAttribute('tabindex', '0'); this.el.dataset.selector = 'true'; this.el.dataset.id = this.id; if (label) { this.el.setAttribute('aria-label', label); this.el.setAttribute('title', label); } var sHtml = this.dotTemplate({ id: this.id }); this.$el.html(sHtml); this._indicatorWidth = this.$el.outerWidth(); this._handleWidth = this.$el.closest('.sliderContent').find('.handle').outerWidth(); // here we assume that timer is inside the widget since it's the persisted value; this._updateHighlightPosition(this.highlightAct.timer, false); this._updateHighlightState(); this._registerEvents(); }, remove: function remove() { this._unregisterEvents(); Indicator.inherited('remove', this, arguments); }, onSliderChange: function onSliderChange() { this._updateHighlightPosition(this.highlightAct.timer, false); }, onScaleChange: function onScaleChange() { this._updateHighlightPosition(this.highlightAct.timer, false); }, onPayloadChange: function onPayloadChange() { this._updateHighlightState(); this._updateTooltipLabel(); }, _updateHighlightState: function _updateHighlightState() { if (this.highlightAct.get('payload').filter(function (item) { return item.operator; }).length) { this.$el.removeClass('empty'); } else { this.$el.addClass('empty'); } }, _updateHighlightPosition: function _updateHighlightPosition(time, isDragging) { // round to the nearest tick for display var tick = this.controller.getTickDuration(); time = Math.round(time / tick) * tick; var position = this.scaleManager.convertTimeToPosition(time); // the center of the icon on the time position -= this._indicatorWidth / 2; // if we are dragging we allow the indicator to be over the handles. if (!isDragging) { position = this._limitPositionToInsideHandles(position); } this.$el.css('left', position + 'px'); return position; }, launchHighlightSummaryDialog: function launchHighlightSummaryDialog() { this.flyoutView = new TimelineHighlightFlyoutController({ dashboardApi: this.dashboardApi, content: this.content, visualization: this.visualization, pageContextAPI: this.pageContextAPI, services: this.services, highlightAct: this.highlightAct, launchView: this, show: this.launchHighlightSummaryDialog, launchPoint: this.$el.get(0) }); this.$el.append(this.flyoutHolder); this._flyout = React.createElement(StorytellingUI.HighlightSummaryFlyout, { anchorElement: this.$el.get(0), closeFlyout: this.closeFlyout.bind(this), itemActions: this.flyoutView.getItemActions(), title: this.stringResources.get('timelineHighlightTitle') }); ReactDOM.render(this._flyout, this.flyoutHolder); }, getPlacement: function getPlacement() { return 'top'; }, _registerEvents: function _registerEvents() { this._dragStartCallback = this._onDragStart.bind(this); this._dragLeftRightCallback = this._onDragLeftRight.bind(this); this._dragEndCallback = this._onDragEnd.bind(this); this._clickCallback = this._onClick.bind(this); this._onKeyPressCallback = this._onKeyPress.bind(this); this.$el.hammer({ correct_for_drag_min_distance: false }).on('dragstart', this._dragStartCallback).on('dragleft', this._dragLeftRightCallback).on('dragright', this._dragLeftRightCallback).on('dragend', this._dragEndCallback); this.$el.find('.svgIcon').on('primaryaction', this._clickCallback); this.$el.on('keydown', this._onKeyPressCallback); }, setTime: function setTime(value) { this._updateHighlightPosition(value, false); }, _unregisterEvents: function _unregisterEvents() { if (this._flyout) { this.closeFlyout(); } if (this.$el) { this.$el.off('dragstart', null, this._dragStartCallback).off('dragleft', null, this._dragLeftRightCallback).off('dragright', null, this._dragLeftRightCallback).off('dragend', null, this._dragEndCallback); this.$el.find('.svgIcon').off('primaryaction', this._clickCallback); this.$el.off('keydown', this._onKeyPressCallback); } }, _onDragLeftRight: function _onDragLeftRight(event) { if (event.gesture) { event.gesture.preventDefault(); event.gesture.stopPropagation(); var position = this._positionFromLeft + event.gesture.deltaX; var time = this.scaleManager.convertPositionToTime(position); time = this._limitTimerToSlider(time); this._updateHighlightPosition(time, true); } }, _onDragEnd: function _onDragEnd(event) { if (event.gesture) { event.gesture.stopPropagation(); this.controller.trigger('timeline:highlightIndicatorDragEnded'); var position = this._positionFromLeft + event.gesture.deltaX; var time = this.scaleManager.convertPositionToTime(position); time = this._limitTimerToSlider(time); this._updateHighlightPosition(time, false); this.controller.updateTimelineHighlight(this.widgetId, this.id, { timer: time }); } this._dragEnd = true; }, _onDragStart: function _onDragStart() { this.closeFlyout(); this.controller.trigger('timeline:highlightIndicatorDragStarted', this.widgetId); var position = this.scaleManager.convertTimeToPosition(this.highlightAct.timer); // the user drags the icon around so we make sure the icon is over the mouse by clamping it to inside the handles // this means that if the timer value was zero and the user moves 1 pixel it could be something like 200 depending on the scale // but nothing stops the user from dragging it back to zero. this._positionFromLeft = this._limitPositionToInsideHandles(position); }, _updateTooltipLabel: function _updateTooltipLabel() { var label = this._getTooltipLabel(); if (label !== this.$el.attr('title')) { this.$el.attr('title', label); this.$el.attr('aria-label', label); } }, _getTooltipLabel: function _getTooltipLabel() { var _this = this; var label = this.stringResources.get('timelineHighlightTitle'); if (this.filterLabelHelper) { var filterItems = this.filterLabelHelper.generateFilterListItems(this.highlightAct.payload); filterItems.forEach(function (filter) { label += '\n'; label += _this.stringResources.get('filter_tooltip', { title: filter.title, description: filter.description }); }); } return label; }, _onClick: function _onClick(event) { if (event.type === 'click' && this._dragEnd) { // Hammer will trigger a ghostclick event after dragend in Firefox this._dragEnd = false; return; } event.stopPropagation(); event.preventDefault(); if (!this.visAPI) { // widget has not been loaded. return var widget = this.widgetHelper.getWidget(this.widgetId); var content = this.widgetHelper.getContent(this.widgetId); if (!widget || !content) { return; } else { this.visAPI = widget.getVisApi && widget.getVisApi(); this.visualization = content.getFeature('Visualization'); } } this.$el.append(this.flyoutHolder); var flyout = React.createElement(StorytellingUI.HighlightFlyout, { widgetId: this.widgetId, actId: this.id, controller: this.controller, anchorElement: this.$el.get(0), closeFlyout: this.closeFlyout.bind(this), openSummaryDialog: this.launchHighlightSummaryDialog.bind(this) }); ReactDOM.render(flyout, this.flyoutHolder); }, _onKeyPress: function _onKeyPress(event) { var $target = $(event.currentTarget); // Set as selected $target.focus(); // Setup the number of pixels to move based on arrow key pressed var xMoveBy = 0; switch (event.keyCode) { case 13: case 32: //Return or Space launches Flyout ODT this._onClick(event); return; case 37: //Left xMoveBy = this.scaleManager.convertPositionToTime(-20); break; case 39: //Right xMoveBy = this.scaleManager.convertPositionToTime(20); break; case 38: case 40: event.stopPropagation(); event.preventDefault(); return; default: return; } event.stopPropagation(); event.preventDefault(); //Moving from the current position. var time = this._limitTimerToSlider(this.highlightAct.timer + xMoveBy); this._updateHighlightPosition(time, false); this.controller.updateTimelineHighlight(this.widgetId, this.id, { timer: time }); var messageOptions = { id: this.id, time: time }; var sMessage = this.stringResources.get('timeline_highlight_moved_to', messageOptions); this._ScreenReader.callOut(sMessage); }, _limitTimerToSlider: function _limitTimerToSlider(timerValue) { timerValue = Math.max(timerValue, this.episodeModel.getEntranceAct().timer + 1); timerValue = Math.min(timerValue, this.episodeModel.getExitAct().timer - 1); return timerValue; }, _limitPositionToInsideHandles: function _limitPositionToInsideHandles(position) { var entranceTime = this.episodeModel.getEntranceAct().timer; var min = this.scaleManager.convertTimeToPosition(entranceTime); min += this._handleWidth; var exitTime = this.episodeModel.getExitAct().timer; var max = this.scaleManager.convertTimeToPosition(exitTime); max -= this._handleWidth + this._indicatorWidth; position = Math.min(position, max); position = Math.max(position, min); // if there is not enough space between the handles we split the difference. var contentWidth = this.scaleManager.convertTimeToPosition(exitTime - entranceTime - this._handleWidth * 2); if (contentWidth < this._indicatorWidth) { position -= (this._indicatorWidth - contentWidth) / 2; } return position; }, closeFlyout: function closeFlyout() { ReactDOM.unmountComponentAtNode(this.flyoutHolder); this.$el.find('.flyout-holder').remove(); } }); return Indicator; }); //# sourceMappingURL=TimelineHighlightIndicatorView.js.map