123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540 |
- 'use strict';
- var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
- function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
- function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
- function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
- /**
- * Licensed Materials - Property of IBM
- * IBM Cognos Products: BI Cloud (C) Copyright IBM Corp. 2018, 2020
- * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
- */
- /**
- *
- * View to swap the Live Widget Visualization through the ODT
- * Options
- * {
- * showTitles: boolean, // Whether to show the titles for the available/recommended visualizations
- * visualizations: {
- * recommended: [], // Array of visualizations to place in recommended section
- * other: [] // Array of visualizations to put in other section
- * },
- * currentVis: String, // The id of the currently selected visualization
- * widget: Live Widget // The widget that is creating the vischanger view
- * }
- *
- **/
- define(['../../../lib/@waca/core-client/js/core-client/ui/core/View', '../../../lib/@waca/core-client/js/core-client/utils/Utils', '../nls/StringResources', 'jquery', 'underscore', 'react-dom', 'react', 'ca-ui-toolkit', 'dashboard-analytics/DynamicFileLoader', 'gemini/app/util/ErrorUtils'], function (BaseView, Utils, resources, $, _, ReactDOM, React, Toolkit, DynamicFileLoader, ErrorUtils) {
- /* eslint react/prop-types: 0 */ // TODO: Remove once we have prop-types brought in from glass.
- var Accordion = Toolkit.Accordion;
- var AccordionItem = Toolkit.AccordionItem;
- var SVGIcon = Toolkit.SVGIcon;
- var Label = Toolkit.Label;
- var SVGIconDecoration = Toolkit.SVGIconDecoration;
- var ProgressIndicator = Toolkit.ProgressIndicator;
- // Limit the number of recommended visualizations to show in the recommended section.
- var MaxRecommendedVisualizationsToShow = 6;
- var AUTO_ID = 'auto';
- /******************************************************
- * Recommended Accordion Section
- * AccordionItem
- * [PropItems] - SVG (with possible deco) and Label
- * All visualizations Accordion Section
- * AccordionItem
- * [PropItems] - SVG (with possible deco) and Label
- ********************************************************/
- /*
- * Class to render the react flyout components
- */
- var ReactComponents = function (_React$Component) {
- _inherits(ReactComponents, _React$Component);
- /*
- * The props hold a state that should be used as this classes state.
- * When the state changes the component gets re-rendered.
- */
- function ReactComponents(props) {
- _classCallCheck(this, ReactComponents);
- var _this = _possibleConstructorReturn(this, _React$Component.call(this, props));
- _this.state = _extends({}, props.state);
- _this.state.checkMarkIcon = props.state.iconsFeature.getIcon('CheckmarkSVG');
- return _this;
- }
- /**
- * @param {string} id of the selected visualization
- * @return {React Component} the decoration if the specified id matched the selected id.
- */
- ReactComponents.prototype.getSelectedDecoration = function getSelectedDecoration(id) {
- // The decoration is basically a checkmark to show the selected vis.
- if (this.state.selectedId === id) {
- return React.createElement(SVGIconDecoration, {
- iconId: this.state.checkMarkIcon.id,
- location: 'bottomRight',
- style: { right: '-4px' } });
- }
- };
- /**
- * @param {object} item describing all the info needed to create a prop item
- * @param {String} dataType either 'Recommended' or 'Other'
- * @return {React Object}
- */
- ReactComponents.prototype.renderPropItem = function renderPropItem(item, dataType) {
- var decoration = this.getSelectedDecoration(item.id);
- var pressed = this.state.selectedId === item.id ? 'true' : 'false';
- return React.createElement(
- 'div',
- { role: 'group', className: 'prop-item' },
- React.createElement(
- 'div',
- { tabIndex: 0,
- 'data-type': dataType,
- className: 'prop-icon vis',
- role: 'button',
- title: item.name,
- 'data-id': item.id,
- 'appcues-data-id': item.id,
- 'aria-label': item.name,
- 'aria-pressed': pressed },
- React.createElement(
- SVGIcon,
- {
- iconId: this.state.svgsMap[item.id].id,
- height: 42,
- width: 42 },
- decoration
- ),
- React.createElement(Label, { className: 'prop-icon-label', label: item.name })
- )
- );
- };
- /**
- * @param {Array} items all the visualizations to add to the row
- * @param {Boolean} isRecommended true iff the Vis Row is the recommended row.
- * @returns {Array} of React components for each Vis in the Vis Row.
- */
- ReactComponents.prototype.loadVisRowItems = function loadVisRowItems(items, isRecommended) {
- var _this2 = this;
- var svgs = [];
- var dataType = isRecommended ? 'Recommended' : 'Other';
- items.forEach(function (item) {
- if (item.id && _this2.state.svgsMap[item.id]) {
- svgs.push(_this2.renderPropItem(item, dataType));
- }
- });
- return svgs;
- };
- /**
- * Render the progress indicator to show we are loading.
- */
- ReactComponents.prototype._renderLoading = function _renderLoading() {
- return React.createElement(
- 'div',
- { className: 'vis-changer-progressIndicator' },
- React.createElement(ProgressIndicator, { size: 'large', variant: 'circle' })
- );
- };
- ReactComponents.prototype._getVisRowItems = function _getVisRowItems(options) {
- var items = options.items,
- isRecommended = options.isRecommended;
- var loadedSvgs = this.loadVisRowItems(items, isRecommended);
- return React.createElement(
- 'div',
- { className: 'visChangerRow' },
- loadedSvgs
- );
- };
- ReactComponents.prototype._getRecommendedAccordionItem = function _getRecommendedAccordionItem() {
- var recommendedOptions = {
- items: this.state.recommended.items,
- isRecommended: true
- };
- return this.state.recommended.isLoading ? this._renderLoading() : this._getVisRowItems(recommendedOptions);
- };
- ReactComponents.prototype._getAllVisAccordionItem = function _getAllVisAccordionItem() {
- var allVisOptions = {
- items: this.state.all.items,
- isRecommended: false
- };
- return this.state.all.isLoading ? this._renderLoading() : this._getVisRowItems(allVisOptions);
- };
- /**
- * Render the collapsible sections (accordions)
- * @return {React Object} representing the Accordion and its sections.
- */
- ReactComponents.prototype._renderAccordions = function _renderAccordions() {
- var recommendedItem = this._getRecommendedAccordionItem();
- var isConsumer = this.state.isConsumer;
- if (!isConsumer) {
- return React.createElement(
- Accordion,
- null,
- React.createElement(
- AccordionItem,
- { itemName: resources.get('recommended_visualizations'), icon: 'left', open: true },
- recommendedItem
- ),
- React.createElement(
- AccordionItem,
- { itemName: resources.get('all_visualizations'), icon: 'left', open: false, onChange: this.state.onItemChange },
- this._getAllVisAccordionItem()
- )
- );
- } else {
- return React.createElement(
- Accordion,
- null,
- React.createElement(
- AccordionItem,
- { itemName: resources.get('recommended_visualizations'), icon: 'left', open: true },
- recommendedItem
- )
- );
- }
- };
- /**
- * @returns {React Object} progress indicator if we are loading, accordions otherwise.
- */
- ReactComponents.prototype.render = function render() {
- return this._renderAccordions();
- };
- return ReactComponents;
- }(React.Component);
- /**
- * The following view is the actual ODT Vis Changer view. It will handle
- * selection of the react components to change vis type, rendering the different
- * choices into two collapsible sections as well as showing a loading indicator
- * until all the SVGs are loaded.
- */
- var View = BaseView.extend({
- events: {
- 'primaryaction .vis': '_selectVis'
- },
- /**
- * Load the SVG files representing all the visualizations
- */
- _loadSVGFileMap: function _loadSVGFileMap() {
- var _this3 = this;
- // If we've already loaded the SVG files, return the map
- if (this.viewState.svgsMap && _.keys(this.viewState.svgsMap).length > 0) {
- return Promise.resolve(this.viewState.svgsMap);
- } else {
- this.viewState.svgsMap = {};
- this._getAllAvailableVisualizations();
- if (this.viewState.all && this.viewState.all.items) {
- var iconsFeature = this.viewState.iconsFeature;
- this.viewState.all.items.forEach(function (item) {
- var visIcon = iconsFeature.getIcon(item.visId);
- _this3.viewState.svgsMap[item.visId] = visIcon;
- });
- if (!(Object.keys(this.viewState.svgsMap).indexOf('auto') !== -1)) {
- var autoIcon = iconsFeature.getIcon('auto');
- this.viewState.svgsMap['auto'] = autoIcon;
- }
- return Promise.resolve(this.viewState.svgsMap);
- }
- }
- },
- init: function init(options) {
- // Provide the basic element.
- this.el = $('<div class=\'visChangerContainer\'/>');
- View.inherited('init', this, arguments);
- this.applyAction = options && options.actions && options.actions.apply;
- var finalOptions = options && options.state ? options.state : options;
- _.extend(this, finalOptions || {});
- this.iconsFeature = this.dashboard.getFeature('Icons');
- this._initializeViewState();
- },
- _elementExists: function _elementExists() {
- var hasLength = false,
- hasKids = false,
- el = this.el;
- if (el) {
- if (el.length) {
- hasLength = el.length > 0;
- }
- if (el.children) {
- hasKids = el.children !== 0;
- }
- }
- return el && (hasKids || hasLength);
- },
- remove: function remove() {
- if (this._elementExists()) {
- ReactDOM.unmountComponentAtNode(this.el);
- }
- this.viewState = null;
- return View.inherited('remove', this, arguments);
- },
- setFocus: function setFocus() {
- this.$('.vis').first().focus();
- },
- _processVisualizations: function _processVisualizations(visualizations) {
- var _this4 = this;
- var markupPayload = [];
- _.each(visualizations, function (vis) {
- _this4.viewState.visualizationMap[vis.id || vis.getId()] = vis;
- markupPayload.push({
- id: vis.id || vis.getId(),
- name: vis.caption || vis.getLabel(),
- visId: vis.id || vis.getId(),
- caption: vis.caption || vis.getLabel(),
- iconUri: vis.icon || vis.getIcon()
- });
- });
- return markupPayload;
- },
- /**
- * We want to dynamically load all the SVGs (why load them if we never need them)
- * This method loads the SVG files into a map, asks the widget for a list
- * of recommended and non-recommended visualizations, and adds the 'Auto'
- * choice as well.
- */
- _preload: function _preload() {
- // Load the file map
- this.viewState.visualizationMap = this.viewState.visualizationMap || {};
- return this._loadSVGFileMap();
- },
- _getAllAvailableVisualizations: function _getAllAvailableVisualizations() {
- var allVisualizations = this.dashboard.getFeature('VisDefinitions').getList();
- var processedItems = this._processVisualizations(allVisualizations);
- this.viewState.all.items = _.sortBy(processedItems, function (vis) {
- return vis.name;
- });
- this.viewState.all.isLoading = false;
- },
- _getRecommendedVisualizations: function _getRecommendedVisualizations() {
- var _this5 = this;
- return this.content.getFeature('Visualization.SmartsRecommender').getRecommendedVisualizations().then(function (recommended) {
- if (_this5.viewState === null) {
- return; //Remove has already been called, stop processing
- }
- var processedVisualizations = _this5._processVisualizations(recommended);
- processedVisualizations.unshift({
- id: AUTO_ID,
- name: resources.get('automaticTypeCaption'),
- visId: AUTO_ID,
- caption: resources.get('automaticTypeCaption'),
- iconUri: 'visualizations-changeVisualization',
- disabled: processedVisualizations.length === 0
- });
- var maxVisualizationsToShow = MaxRecommendedVisualizationsToShow;
- if (_this5.viewState.selectedId) {
- // Don't include Auto in the limit
- maxVisualizationsToShow++;
- }
- _this5.viewState.recommended.items = processedVisualizations.slice(0, maxVisualizationsToShow);
- _this5.viewState.recommended.isLoading = false;
- });
- },
- _initializeViewState: function _initializeViewState() {
- this.viewState = {
- recommended: {
- items: [],
- isLoading: true
- },
- all: {
- items: [],
- isLoading: true
- },
- svgsMap: null,
- selectedId: this._getSelectedVizId(),
- visualizationMap: null,
- iconsFeature: this.iconsFeature,
- onItemChange: this._onItemChange.bind(this)
- };
- },
- //called when an item in the accordian is changed
- _onItemChange: function _onItemChange() {
- //ensure this flyout view is not off the screen
- var diff = this._getOffScreenHeight();
- if (diff > 0) {
- //TODO: This is not a great solution, especially if we change the way popovers are handled.
- // in the future, any resizing should be handled by the popover class.
- var popover = $(this.el).parents('.popover');
- var top = Math.max(0, popover.position().top - diff);
- popover && popover.css({ top: top });
- }
- },
- /**
- * Checks to see if this View is off the visible screen.
- * @returns the number of pixels this view is off the screen, or -1 if it is not off the screen
- */
- _getOffScreenHeight: function _getOffScreenHeight() {
- var htmlScrollHeight = this._getDocumentElement().scrollHeight;
- var innerHeight = Utils.getCurrentWindow().innerHeight;
- if (htmlScrollHeight > innerHeight) {
- return htmlScrollHeight - innerHeight;
- }
- return -1;
- },
- /**
- * @returns The document.documentElement.
- * This is here to more easily mock for tests.
- */
- _getDocumentElement: function _getDocumentElement() {
- return document.documentElement;
- },
- _getSelectedVizId: function _getSelectedVizId() {
- var visualization = this.content.getFeature('Visualization');
- if (this.currentVis && this.currentVis === AUTO_ID || visualization && !visualization.isTypeLocked()) {
- return AUTO_ID;
- } else {
- return visualization.getDefinition().getId();
- }
- },
- _resetRecommendedViewState: function _resetRecommendedViewState() {
- this.viewState.recommended = {
- items: [],
- isLoading: true
- };
- if (this._elementExists() && this.reactComponents) {
- ReactDOM.unmountComponentAtNode(this.el);
- }
- },
- /**
- * Create the react components with an initial loading state.
- */
- _showLoading: function _showLoading() {
- this._resetRecommendedViewState();
- // Render the react components
- this.reactComponents = ReactDOM.render(React.createElement(ReactComponents, { state: this.viewState }), this.el);
- },
- /**
- * Render the react components with a state to show the accordions.
- */
- _showReactSections: function _showReactSections() {
- this.reactComponents.setState(JSON.parse(JSON.stringify(this.viewState)));
- },
- _render: function _render(options) {
- var _this6 = this;
- _.extend(this, options || {});
- this._isConsumer();
- this._showLoading();
- return this._preload().then(function () {
- _this6._showReactSections();
- }).then(this._getRecommendedVisualizations.bind(this)).then(function () {
- _this6._showReactSections();
- });
- },
- _isConsumer: function _isConsumer() {
- var currentContentView = this.dashboard && this.dashboard.getCurrentContentView();
- var glassContext = currentContentView && currentContentView.glassContext;
- if (this.viewState && glassContext) {
- this.viewState.isConsumer = !ErrorUtils.hasCapability(glassContext, 'canAuthorDashboard');
- }
- },
- /**
- * Method to render the view. This includes creating the react components to
- * initially show a progress indicator, preload all the svg icons, divide the
- * choices into sections, then update the react components to show the accordions.
- */
- render: function render(options) {
- this._render(options);
- return Promise.resolve();
- },
- _getTargetFromEvent: function _getTargetFromEvent(event) {
- return $(event.currentTarget);
- },
- // when id auto we highlight auto too
- /**
- * Event handler for a .vis button is clicked.
- * Send event to be handled by Live Widget and updates its view with current selection
- */
- _selectVis: function _selectVis(event) {
- var $currentTarget = this._getTargetFromEvent(event);
- var id = $currentTarget.attr('data-id');
- var recommendedCategory = $currentTarget.attr('data-type');
- // If selected visualization target is same as current visualization target
- if (this.viewState.selectedId === id) {
- return;
- }
- this.viewState.selectedId = id;
- this.reactComponents.setState({ selectedId: id });
- var visDefinition = this.dashboard.getFeature('VisDefinitions').getById(id);
- var visType = visDefinition && visDefinition.getType() || id;
- this.applyAction(visType, { recommendedCategory: recommendedCategory });
- if (this.selectVisCB) {
- this.selectVisCB();
- }
- }
- });
- return View;
- });
- //# sourceMappingURL=VisChangerFlyoutView.js.map
|