@gooddata/react-components
Version:
GoodData.UI - A powerful JavaScript library for building analytical applications
343 lines • 16.6 kB
JavaScript
;
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