UNPKG

@gooddata/react-components

Version:

GoodData.UI - A powerful JavaScript library for building analytical applications

343 lines • 16.6 kB
"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; Object.defineProperty(exports, "__esModule", { value: true }); // (C) 2007-2020 GoodData Corporation var React = require("react"); var classnames_1 = require("classnames"); var get = require("lodash/get"); var isEqual = require("lodash/isEqual"); var noop = require("lodash/noop"); var mapboxgl = require("mapbox-gl"); var geoChartDataLayers_1 = require("./geoChartDataLayers"); var geoChartDataSource_1 = require("./geoChartDataSource"); var geoChart_1 = require("../../../constants/geoChart"); require("../../../../styles/css/geoChart.css"); var geoChartTooltip_1 = require("./geoChartTooltip"); var viewport_1 = require("../../../helpers/geoChart/viewport"); var common_1 = require("../../../helpers/geoChart/common"); var drilldownEventing_1 = require("../../visualizations/utils/drilldownEventing"); var GeoChartRenderer = /** @class */ (function (_super) { __extends(GeoChartRenderer, _super); function GeoChartRenderer(props) { var _this = _super.call(this, props) || this; _this.setChartRef = function (ref) { _this.chartRef = ref; }; _this.createMap = function () { var _a = _this.props, config = _a.config, data = _a.geoData.location.data; var _b = (config || {}).isExportMode, isExportMode = _b === void 0 ? false : _b; var isViewportFrozen = _this.isViewportFrozen(); _this.chart = new mapboxgl.Map(__assign({}, geoChart_1.DEFAULT_MAPBOX_OPTIONS, viewport_1.getViewportOptions(data, config), { container: _this.chartRef, // If true, the map’s canvas can be exported to a PNG using map.getCanvas().toDataURL(). // This is false by default as a performance optimization. interactive: !isViewportFrozen, preserveDrawingBuffer: isExportMode })); }; _this.updateMapWithConfig = function (prevConfig, prevColorStrategy) { if (_this.shouldResetMap(prevConfig, prevColorStrategy)) { // Config for clustering and pushpin size lead to change layer setting // Then calling resetMap here is needed _this.resetMap(); } _this.updatePanAndZoom(); _this.updateViewport(prevConfig); }; _this.resetMap = function () { _this.cleanupMap(); _this.setupMap(); }; _this.shouldResetMap = function (prevConfig, prevColorStrategy) { var _a = _this.props, colorStrategy = _a.colorStrategy, config = _a.config; if (common_1.isPointsConfigChanged(prevConfig.points, config.points)) { return true; } if (common_1.isColorAssignmentItemChanged(prevColorStrategy.getColorAssignment(), colorStrategy.getColorAssignment())) { return true; } return false; }; _this.isViewportFrozen = function () { var config = _this.props.config; return get(config, "viewport.frozen", false); }; _this.removeMapControls = function () { if (_this.navigationControlButton) { _this.chart.removeControl(_this.navigationControlButton); _this.navigationControlButton = null; } }; _this.addMapControls = function () { if (!_this.navigationControlButton) { _this.navigationControlButton = new mapboxgl.NavigationControl({ showCompass: false, }); _this.chart.addControl(_this.navigationControlButton, "bottom-right"); } }; _this.toggleMapControls = function () { var isViewportFrozen = _this.isViewportFrozen(); if (!isViewportFrozen) { _this.addMapControls(); } else { _this.removeMapControls(); } }; _this.toggleInteractionEvents = function () { var isViewportFrozen = _this.isViewportFrozen(); if (!_this.chart) { return; } var action = isViewportFrozen ? "disable" : "enable"; geoChart_1.INTERACTION_EVENTS.forEach(function (interactionEvent) { return _this.chart[interactionEvent][action](); }); }; _this.updatePanAndZoom = function () { _this.toggleMapControls(); _this.toggleInteractionEvents(); }; _this.updateViewport = function (prevConfig) { var _a = _this.props, config = _a.config, data = _a.geoData.location.data; var prevViewport = prevConfig.viewport; var viewport = config.viewport; if (isEqual(prevViewport, viewport)) { return; } var bounds = viewport_1.getViewportOptions(data, config).bounds; if (bounds) { _this.chart.fitBounds(bounds, geoChart_1.DEFAULT_MAPBOX_OPTIONS.fitBoundsOptions); } }; _this.setFilterMap = function () { var selectedSegmentItems = _this.props.config.selectedSegmentItems; if (_this.chart.getLayer(geoChart_1.DEFAULT_LAYER_NAME)) { _this.chart.setFilter(geoChart_1.DEFAULT_LAYER_NAME, geoChartDataLayers_1.createPushpinFilter(selectedSegmentItems)); } }; _this.handleMapEvent = function () { var chart = _this.chart; chart.on("click", geoChart_1.DEFAULT_LAYER_NAME, _this.handleMapClick); chart.on("idle", _this.handleMapIdle); chart.on("load", _this.setupMap); chart.on("load", _this.adjustChartHeight); chart.on("mouseenter", geoChart_1.DEFAULT_LAYER_NAME, _this.handlePushpinMouseEnter); chart.on("mouseleave", geoChart_1.DEFAULT_LAYER_NAME, _this.handlePushpinMouseLeave); chart.on("moveend", _this.handlePushpinMoveEnd); chart.on("zoomend", _this.handlePushpinZoomEnd); }; /* Fired after the last frame rendered before the map enters an "idle" state: - No camera transitions are in progress - All currently requested tiles have loaded - All fade/transition animations have completed This is called one time only */ _this.handleMapIdle = function () { var _a = _this, chart = _a.chart, afterRender = _a.props.afterRender; chart.off("idle", _this.handleMapIdle); afterRender(); }; _this.setupMap = function () { var _a = _this, chart = _a.chart, handleLayerLoaded = _a.handleLayerLoaded, props = _a.props; var colorStrategy = props.colorStrategy, config = props.config, geoData = props.geoData; var _b = config || {}, _c = _b.points, _d = (_c === void 0 ? {} : _c).groupNearbyPoints, groupNearbyPoints = _d === void 0 ? true : _d, _e = _b.showLabels, showLabels = _e === void 0 ? true : _e; var hasClustering = common_1.isClusteringAllowed(geoData, groupNearbyPoints); var dataSourceProps = { colorStrategy: colorStrategy, config: config, geoData: geoData, hasClustering: hasClustering, }; chart.addSource(geoChart_1.DEFAULT_DATA_SOURCE_NAME, geoChartDataSource_1.createPushpinDataSource(dataSourceProps)); if (!hasClustering) { chart.addLayer(geoChartDataLayers_1.createPushpinDataLayer(geoChart_1.DEFAULT_DATA_SOURCE_NAME, geoData, config), "state-label"); } else { chart.addLayer(geoChartDataLayers_1.createClusterPoints(geoChart_1.DEFAULT_DATA_SOURCE_NAME)); chart.addLayer(geoChartDataLayers_1.createClusterLabels(geoChart_1.DEFAULT_DATA_SOURCE_NAME)); // un-clustered points will be rendered under state/county label chart.addLayer(geoChartDataLayers_1.createUnclusterPoints(geoChart_1.DEFAULT_DATA_SOURCE_NAME), "state-label"); } // that config is not public, // we only use for storybook to make it is more stable if (!showLabels) { var _f = chart.getStyle().layers, layers = _f === void 0 ? [] : _f; layers.forEach(function (layer) { if (layer.id.includes(geoChart_1.LAYER_STYLE_LABEL_PREFIX)) { _this.removeLayer(layer.id); } }); } // keep listening to the data event until the style is loaded chart.on("data", handleLayerLoaded); }; _this.adjustChartHeight = function () { var _a = _this, chart = _a.chart, chartRef = _a.chartRef; if (!chartRef) { return; } var chartHeight = chartRef.clientHeight; var parentHeight = chartRef.parentElement.clientHeight; var shouldResize = chartHeight <= geoChart_1.ZOOM_CONTROLS_HEIGHT && geoChart_1.ZOOM_CONTROLS_HEIGHT <= parentHeight; if (shouldResize) { // set min height to re-position mapbox attribution and zoom control, in case there are too many top legend items // that take all visible height of widget and make geo chart container's height zero chartRef.style.minHeight = parentHeight + "px"; chart.resize(); } }; _this.handleLayerLoaded = function () { var chart = _this.chart; if (!chart.isStyleLoaded()) { return; } chart.off("data", _this.handleLayerLoaded); }; _this.createTooltip = function () { _this.tooltip = new mapboxgl.Popup(geoChart_1.DEFAULT_TOOLTIP_OPTIONS); }; _this.cleanupMap = function () { _this.removeLayer(geoChart_1.DEFAULT_LAYER_NAME); _this.removeLayer(geoChart_1.DEFAULT_CLUSTER_LAYER_NAME); _this.removeLayer(geoChart_1.DEFAULT_CLUSTER_LABELS_CONFIG.id); if (_this.chart.getSource(geoChart_1.DEFAULT_DATA_SOURCE_NAME)) { _this.chart.removeSource(geoChart_1.DEFAULT_DATA_SOURCE_NAME); } }; _this.removeMap = function () { if (!_this.chart) { return; } // try catch to hide the mapbox's error message // TypeError: Cannot read property 'off' of undefined // mapbox is trying to call its function after deleted // https://github.com/mapbox/mapbox-gl-js/blob/master/src/ui/control/navigation_control.js#L118 try { _this.chart.remove(); } catch (_a) { return; } }; _this.handlePushpinMoveEnd = function (e) { var target = e.target; var onCenterPositionChanged = _this.props.onCenterPositionChanged; var _a = target.getCenter(), lng = _a.lng, lat = _a.lat; var center = { lng: lng, lat: lat }; onCenterPositionChanged(center); }; _this.handlePushpinZoomEnd = function (e) { var target = e.target; var onZoomChanged = _this.props.onZoomChanged; var zoom = target.getZoom(); onZoomChanged(zoom); }; _this.handleMapClick = function (e) { var _a = _this.props, viewport = _a.config.viewport, drillableItems = _a.drillableItems, drillConfig = _a.drillConfig, execution = _a.execution, geoData = _a.geoData; var features = e.features, originalEvent = e.originalEvent; var _b = features[0], coordinates = _b.geometry.coordinates, properties = _b.properties; // Disable drilling in edit/export mode if (get(viewport, "frozen", false)) { return; } // without this, the drill dialog will be closed originalEvent.stopPropagation(); return drilldownEventing_1.handleGeoPushpinDrillEvent(drillableItems, drillConfig, execution, geoData, properties, coordinates, originalEvent.target); }; _this.handlePushpinMouseEnter = function (e) { var _a = _this, chart = _a.chart, props = _a.props, tooltip = _a.tooltip; var config = props.config; return geoChartTooltip_1.handlePushpinMouseEnter(e, chart, tooltip, config); }; _this.handlePushpinMouseLeave = function (e) { var _a = _this, chart = _a.chart, props = _a.props, tooltip = _a.tooltip; var config = props.config; return geoChartTooltip_1.handlePushpinMouseLeave(e, chart, tooltip, config); }; mapboxgl.accessToken = props.config.mapboxToken; return _this; } GeoChartRenderer.prototype.componentDidUpdate = function (prevProps) { var _a = this.props, selectedSegmentItems = _a.config.selectedSegmentItems, colorStrategy = _a.colorStrategy; var prevConfig = prevProps.config, prevColorStrategy = prevProps.colorStrategy; var _b = (prevConfig || {}).selectedSegmentItems, prevSelectedSegmentItems = _b === void 0 ? [] : _b; // reisze map when component is updated // for exemple: toggle legend, change position of legend this.chart.resize(); // only update map when style is ready // work around for ticket SD-898 // avoid refresh whole map will be fixed in ticket SD-899 if (!this.chart.isStyleLoaded()) { return; } var isColorChanged = common_1.isColorAssignmentItemChanged(prevColorStrategy.getColorAssignment(), colorStrategy.getColorAssignment()); var selectedSegmentItemsChanged = selectedSegmentItems && !isEqual(selectedSegmentItems, prevSelectedSegmentItems); if (!isColorChanged && selectedSegmentItemsChanged) { return this.setFilterMap(); } this.updateMapWithConfig(prevConfig, prevColorStrategy); }; GeoChartRenderer.prototype.componentDidMount = function () { this.createTooltip(); this.createMap(); this.createMapControls(); this.handleMapEvent(); }; GeoChartRenderer.prototype.componentWillUnmount = function () { this.removeMap(); }; GeoChartRenderer.prototype.render = function () { var _a = this.props.config.isExportMode, isExportMode = _a === void 0 ? false : _a; var classNames = classnames_1.default("s-gd-geo-chart-renderer", "mapbox-container", { isExportMode: isExportMode, "s-isExportMode": isExportMode, }); return React.createElement("div", { className: classNames, ref: this.setChartRef }); }; GeoChartRenderer.prototype.createMapControls = function () { var isViewportFrozen = this.isViewportFrozen(); this.chart.addControl(new mapboxgl.AttributionControl({ compact: true, })); if (!isViewportFrozen) { this.addMapControls(); } }; GeoChartRenderer.prototype.removeLayer = function (layerName) { if (this.chart.getLayer(layerName)) { this.chart.removeLayer(layerName); } }; GeoChartRenderer.defaultProps = { config: { mapboxToken: "", }, afterRender: noop, onZoomChanged: noop, onCenterPositionChanged: noop, }; return GeoChartRenderer; }(React.Component)); exports.default = GeoChartRenderer; //# sourceMappingURL=GeoChartRenderer.js.map