svgmap 2.19.3 → 2.20.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/dist/index.cjs +340 -143
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +340 -143
- package/dist/index.js.map +1 -1
- package/dist/svg-map.css +61 -21
- package/dist/svg-map.min.css +1 -1
- package/dist/svg-map.umd.js +340 -143
- package/dist/svg-map.umd.js.map +1 -1
- package/dist/svg-map.umd.min.js +1 -1
- package/dist/svgMap.css +61 -21
- package/dist/svgMap.js +340 -143
- package/dist/svgMap.js.map +1 -1
- package/dist/svgMap.min.css +1 -1
- package/dist/svgMap.min.js +1 -1
- package/package.json +4 -4
- package/src/js/core/svg-map.js +340 -143
- package/src/scss/map.scss +4 -0
- package/src/scss/tooltip.scss +26 -6
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svgmap",
|
|
3
3
|
"description": "svgMap is a JavaScript library that lets you easily create an interactable world map comparing customizable data for each country.",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.20.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"keywords": [
|
|
@@ -43,12 +43,12 @@
|
|
|
43
43
|
"prepublishOnly": "npm run build && node test/assets.js"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"@rollup/plugin-commonjs": "^29.0.
|
|
46
|
+
"@rollup/plugin-commonjs": "^29.0.3",
|
|
47
47
|
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
48
48
|
"@rollup/plugin-terser": "^1.0.0",
|
|
49
49
|
"csso": "^5.0.5",
|
|
50
|
-
"rollup": "^4.
|
|
50
|
+
"rollup": "^4.61.0",
|
|
51
51
|
"rollup-plugin-postcss": "^4.0.2",
|
|
52
|
-
"sass": "^1.
|
|
52
|
+
"sass": "^1.100.0"
|
|
53
53
|
}
|
|
54
54
|
}
|
package/src/js/core/svg-map.js
CHANGED
|
@@ -70,6 +70,15 @@ export default class svgMap {
|
|
|
70
70
|
// Set to true to open the link on mobile devices, set to false (default) to show the tooltip
|
|
71
71
|
touchLink: false,
|
|
72
72
|
|
|
73
|
+
// When false, disables hover/touch-following tooltips (not the on-map persistent labels; see persistentTooltips)
|
|
74
|
+
showTooltips: true,
|
|
75
|
+
|
|
76
|
+
// 'hover' (default): mouse shows tooltip on enter. 'click': mouse opens tooltip on click; touch/pen unchanged.
|
|
77
|
+
tooltipTrigger: 'hover',
|
|
78
|
+
|
|
79
|
+
// Persistent on-map tooltips: an array of country IDs, or a function (countryID, countryValues) => boolean
|
|
80
|
+
persistentTooltips: false,
|
|
81
|
+
|
|
73
82
|
// Set to true to show the to show a zoom reset button
|
|
74
83
|
showZoomReset: false,
|
|
75
84
|
|
|
@@ -78,6 +87,10 @@ export default class svgMap {
|
|
|
78
87
|
return null;
|
|
79
88
|
},
|
|
80
89
|
|
|
90
|
+
// Called on country click (pointer released without dragging). Receives
|
|
91
|
+
// (countryID, event). Return false to skip opening data.values[*].link.
|
|
92
|
+
onCountryClick: null,
|
|
93
|
+
|
|
81
94
|
// Country specific options
|
|
82
95
|
countries: {
|
|
83
96
|
// Western Sahara: Set to false to combine Morocco (MA) and Western Sahara (EH)
|
|
@@ -117,6 +130,9 @@ export default class svgMap {
|
|
|
117
130
|
// Wrapper element
|
|
118
131
|
this.wrapper = document.getElementById(this.options.targetElementID);
|
|
119
132
|
this.wrapper.classList.add('svgMap-wrapper');
|
|
133
|
+
if (typeof this.options.onCountryClick === 'function') {
|
|
134
|
+
this.wrapper.classList.add('svgMap-country-click-callback');
|
|
135
|
+
}
|
|
120
136
|
|
|
121
137
|
// Container element
|
|
122
138
|
this.container = document.createElement('div');
|
|
@@ -808,7 +824,9 @@ export default class svgMap {
|
|
|
808
824
|
|
|
809
825
|
createMap() {
|
|
810
826
|
// Create the tooltip
|
|
811
|
-
this.
|
|
827
|
+
if (this.options.showTooltips) {
|
|
828
|
+
this.createTooltip();
|
|
829
|
+
}
|
|
812
830
|
|
|
813
831
|
// Create map wrappers
|
|
814
832
|
this.mapWrapper = this.createElement(
|
|
@@ -934,6 +952,103 @@ export default class svgMap {
|
|
|
934
952
|
}.bind(this);
|
|
935
953
|
|
|
936
954
|
// Add map elements
|
|
955
|
+
var countryElements = [];
|
|
956
|
+
|
|
957
|
+
const clearActive = function clearActive() {
|
|
958
|
+
this.mapImage
|
|
959
|
+
.querySelectorAll('.svgMap-active')
|
|
960
|
+
.forEach((el) => el.classList.remove('svgMap-active'));
|
|
961
|
+
}.bind(this);
|
|
962
|
+
|
|
963
|
+
const isClickTooltip =
|
|
964
|
+
this.options.showTooltips && this.options.tooltipTrigger === 'click';
|
|
965
|
+
|
|
966
|
+
const getCountryFromEvent = function (e) {
|
|
967
|
+
return e.target && e.target.closest
|
|
968
|
+
? e.target.closest('.svgMap-country')
|
|
969
|
+
: null;
|
|
970
|
+
};
|
|
971
|
+
|
|
972
|
+
const raiseCountry = function (countryElement, setActive) {
|
|
973
|
+
if (setActive) {
|
|
974
|
+
clearActive();
|
|
975
|
+
}
|
|
976
|
+
countryElement.parentNode.insertBefore(
|
|
977
|
+
countryElement,
|
|
978
|
+
this.persistentTooltipGroup || null
|
|
979
|
+
);
|
|
980
|
+
if (setActive) {
|
|
981
|
+
countryElement.classList.add('svgMap-active');
|
|
982
|
+
}
|
|
983
|
+
}.bind(this);
|
|
984
|
+
|
|
985
|
+
const showCountryTooltip = function (countryElement, e, setActive) {
|
|
986
|
+
raiseCountry(countryElement, setActive);
|
|
987
|
+
this.setTooltipContent(this.getTooltipContent(countryElement.dataset.id));
|
|
988
|
+
this.showTooltip(e);
|
|
989
|
+
}.bind(this);
|
|
990
|
+
|
|
991
|
+
// Touch only: preview tooltip on finger down without marking country active
|
|
992
|
+
// (active is set on pointerup so link countries keep two-tap navigation)
|
|
993
|
+
this.mapImage.addEventListener(
|
|
994
|
+
'pointerdown',
|
|
995
|
+
(e) => {
|
|
996
|
+
if (!this.options.showTooltips || e.pointerType !== 'touch') {
|
|
997
|
+
return;
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
const countryElement = getCountryFromEvent(e);
|
|
1001
|
+
if (!countryElement) {
|
|
1002
|
+
this.hideTooltip();
|
|
1003
|
+
return;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
showCountryTooltip(countryElement, e, false);
|
|
1007
|
+
this.moveTooltip(e);
|
|
1008
|
+
},
|
|
1009
|
+
{ passive: true }
|
|
1010
|
+
);
|
|
1011
|
+
|
|
1012
|
+
this.mapImage.addEventListener(
|
|
1013
|
+
'pointercancel',
|
|
1014
|
+
(e) => {
|
|
1015
|
+
if (e.pointerType === 'touch') {
|
|
1016
|
+
this.hideTooltip();
|
|
1017
|
+
}
|
|
1018
|
+
},
|
|
1019
|
+
{ passive: true }
|
|
1020
|
+
);
|
|
1021
|
+
|
|
1022
|
+
// Hover (mouse/pen) and touch drag: raise country + optional floating tooltip
|
|
1023
|
+
this.mapImage.addEventListener(
|
|
1024
|
+
'pointermove',
|
|
1025
|
+
(e) => {
|
|
1026
|
+
const countryElement = getCountryFromEvent(e);
|
|
1027
|
+
if (!countryElement) {
|
|
1028
|
+
clearActive();
|
|
1029
|
+
if (this.options.showTooltips) {
|
|
1030
|
+
this.hideTooltip();
|
|
1031
|
+
}
|
|
1032
|
+
return;
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
const mouseClickMode = e.pointerType === 'mouse' && isClickTooltip;
|
|
1036
|
+
|
|
1037
|
+
// Always raise hovered country (SVG paint order + .svgMap-active stroke)
|
|
1038
|
+
if (!this.options.showTooltips || mouseClickMode) {
|
|
1039
|
+
raiseCountry(countryElement, true);
|
|
1040
|
+
return;
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
showCountryTooltip(countryElement, e, true);
|
|
1044
|
+
|
|
1045
|
+
if (e.pointerType === 'touch') {
|
|
1046
|
+
this.moveTooltip(e);
|
|
1047
|
+
}
|
|
1048
|
+
},
|
|
1049
|
+
{ passive: true }
|
|
1050
|
+
);
|
|
1051
|
+
|
|
937
1052
|
Object.keys(mapPaths).forEach(
|
|
938
1053
|
function (countryID) {
|
|
939
1054
|
var countryData = this.mapPaths[countryID];
|
|
@@ -951,156 +1066,50 @@ export default class svgMap {
|
|
|
951
1066
|
'id',
|
|
952
1067
|
this.id + '-map-country-' + countryID
|
|
953
1068
|
);
|
|
954
|
-
countryElement.
|
|
1069
|
+
countryElement.dataset.id = countryID;
|
|
955
1070
|
countryElement.classList.add('svgMap-country');
|
|
956
1071
|
|
|
957
1072
|
this.mapImage.appendChild(countryElement);
|
|
958
|
-
|
|
959
|
-
// Add tooltip when touch is used
|
|
960
|
-
function handlePointerMove(e) {
|
|
961
|
-
if (e.pointerType === 'touch') return;
|
|
962
|
-
|
|
963
|
-
const target = document.elementFromPoint(e.clientX, e.clientY);
|
|
964
|
-
|
|
965
|
-
if (
|
|
966
|
-
!target ||
|
|
967
|
-
(!target.closest('.svgMap-country') &&
|
|
968
|
-
!target.closest('.svgMap-tooltip'))
|
|
969
|
-
) {
|
|
970
|
-
this.hideTooltip();
|
|
971
|
-
document
|
|
972
|
-
.querySelectorAll('.svgMap-active')
|
|
973
|
-
.forEach(el => el.classList.remove('svgMap-active'));
|
|
974
|
-
}
|
|
975
|
-
}
|
|
976
|
-
|
|
977
|
-
const handlePointerMoveBound = handlePointerMove.bind(this);
|
|
978
|
-
|
|
979
|
-
countryElement.addEventListener(
|
|
980
|
-
'pointerenter',
|
|
981
|
-
function (e) {
|
|
982
|
-
// Only add pointermove listener for non-touch pointers
|
|
983
|
-
if (e.pointerType !== 'touch') {
|
|
984
|
-
document.addEventListener(
|
|
985
|
-
'pointermove',
|
|
986
|
-
handlePointerMoveBound,
|
|
987
|
-
{ passive: true }
|
|
988
|
-
);
|
|
989
|
-
}
|
|
990
|
-
|
|
991
|
-
document
|
|
992
|
-
.querySelectorAll('.svgMap-active')
|
|
993
|
-
.forEach(el => el.classList.remove('svgMap-active'));
|
|
994
|
-
|
|
995
|
-
countryElement.parentNode.appendChild(countryElement);
|
|
996
|
-
countryElement.classList.add('svgMap-active');
|
|
997
|
-
|
|
998
|
-
const countryID = countryElement.getAttribute('data-id');
|
|
999
|
-
this.setTooltipContent(this.getTooltipContent(countryID));
|
|
1000
|
-
this.showTooltip(e);
|
|
1001
|
-
|
|
1002
|
-
// For touch, move tooltip to the touch position and keep it there
|
|
1003
|
-
if (e.pointerType === 'touch') {
|
|
1004
|
-
this.moveTooltip(e);
|
|
1005
|
-
}
|
|
1006
|
-
}.bind(this)
|
|
1007
|
-
);
|
|
1008
|
-
|
|
1009
|
-
// Handle touch move - update tooltip position while panning
|
|
1010
|
-
countryElement.addEventListener(
|
|
1011
|
-
'touchmove',
|
|
1012
|
-
function (e) {
|
|
1013
|
-
this.moveTooltip(e);
|
|
1014
|
-
}.bind(this),
|
|
1015
|
-
{ passive: true }
|
|
1016
|
-
);
|
|
1017
|
-
|
|
1018
|
-
// Handle touch end - remove active state and hide tooltip
|
|
1019
|
-
countryElement.addEventListener(
|
|
1020
|
-
'touchend',
|
|
1021
|
-
function (e) {
|
|
1022
|
-
const touch = e.changedTouches[0];
|
|
1023
|
-
const elementAtEnd = document.elementFromPoint(touch.clientX, touch.clientY);
|
|
1024
|
-
|
|
1025
|
-
// Only hide if touch ended outside the country or tooltip
|
|
1026
|
-
if (
|
|
1027
|
-
!elementAtEnd ||
|
|
1028
|
-
(!elementAtEnd.closest('.svgMap-country') &&
|
|
1029
|
-
!elementAtEnd.closest('.svgMap-tooltip'))
|
|
1030
|
-
) {
|
|
1031
|
-
this.hideTooltip();
|
|
1032
|
-
document
|
|
1033
|
-
.querySelectorAll('.svgMap-active')
|
|
1034
|
-
.forEach(el => el.classList.remove('svgMap-active'));
|
|
1035
|
-
}
|
|
1036
|
-
}.bind(this),
|
|
1037
|
-
{ passive: true }
|
|
1038
|
-
);
|
|
1039
|
-
|
|
1040
|
-
// Remove pointermove listener when leaving non-touch pointer
|
|
1041
|
-
countryElement.addEventListener(
|
|
1042
|
-
'pointerleave',
|
|
1043
|
-
function (e) {
|
|
1044
|
-
if (e.pointerType !== 'touch') {
|
|
1045
|
-
document.removeEventListener('pointermove', handlePointerMoveBound);
|
|
1046
|
-
this.hideTooltip();
|
|
1047
|
-
document
|
|
1048
|
-
.querySelectorAll('.svgMap-active')
|
|
1049
|
-
.forEach(el => el.classList.remove('svgMap-active'));
|
|
1050
|
-
}
|
|
1051
|
-
}.bind(this)
|
|
1052
|
-
);
|
|
1053
|
-
|
|
1054
|
-
document.addEventListener(
|
|
1055
|
-
'pointerover',
|
|
1056
|
-
function (e) {
|
|
1057
|
-
if (e.pointerType !== 'touch') return;
|
|
1058
|
-
|
|
1059
|
-
if (
|
|
1060
|
-
e.target.closest('.svgMap-country') ||
|
|
1061
|
-
e.target.closest('.svgMap-tooltip')
|
|
1062
|
-
) {
|
|
1063
|
-
return;
|
|
1064
|
-
}
|
|
1065
|
-
|
|
1066
|
-
this.hideTooltip();
|
|
1067
|
-
document
|
|
1068
|
-
.querySelectorAll('.svgMap-active')
|
|
1069
|
-
.forEach(el => el.classList.remove('svgMap-active'));
|
|
1070
|
-
}.bind(this),
|
|
1071
|
-
{ passive: true }
|
|
1072
|
-
);
|
|
1073
|
+
countryElements.push(countryElement);
|
|
1073
1074
|
|
|
1074
1075
|
if (
|
|
1075
1076
|
this.options.data.values &&
|
|
1076
1077
|
this.options.data.values[countryID] &&
|
|
1077
1078
|
this.options.data.values[countryID]['link']
|
|
1078
1079
|
) {
|
|
1079
|
-
countryElement.
|
|
1080
|
-
'
|
|
1081
|
-
this.options.data.values[countryID]['link']
|
|
1082
|
-
);
|
|
1080
|
+
countryElement.dataset.link =
|
|
1081
|
+
this.options.data.values[countryID]['link'];
|
|
1083
1082
|
if (this.options.data.values[countryID]['linkTarget']) {
|
|
1084
|
-
countryElement.
|
|
1085
|
-
|
|
1086
|
-
this.options.data.values[countryID]['linkTarget']
|
|
1087
|
-
);
|
|
1083
|
+
countryElement.dataset.linkTarget =
|
|
1084
|
+
this.options.data.values[countryID]['linkTarget'];
|
|
1088
1085
|
}
|
|
1089
1086
|
}
|
|
1090
1087
|
}.bind(this)
|
|
1091
1088
|
);
|
|
1092
1089
|
|
|
1090
|
+
var persistent = this.options.persistentTooltips;
|
|
1091
|
+
if (
|
|
1092
|
+
persistent &&
|
|
1093
|
+
(Array.isArray(persistent) || typeof persistent === 'function')
|
|
1094
|
+
) {
|
|
1095
|
+
this.createPersistentTooltips(countryElements);
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1093
1098
|
let pointerStart = null;
|
|
1094
1099
|
let activeCountry = null;
|
|
1095
1100
|
|
|
1096
|
-
this.mapImage.addEventListener(
|
|
1097
|
-
|
|
1098
|
-
|
|
1101
|
+
this.mapImage.addEventListener(
|
|
1102
|
+
'pointerdown',
|
|
1103
|
+
(e) => {
|
|
1104
|
+
// Ignore right click (on desktop it allows inspecting the chart elements without opening the URL)
|
|
1105
|
+
if (e.button !== 0) return;
|
|
1099
1106
|
|
|
1100
|
-
|
|
1101
|
-
|
|
1107
|
+
pointerStart = { x: e.clientX, y: e.clientY };
|
|
1108
|
+
},
|
|
1109
|
+
{ passive: true }
|
|
1110
|
+
);
|
|
1102
1111
|
|
|
1103
|
-
this.mapImage.addEventListener('pointerup', e => {
|
|
1112
|
+
this.mapImage.addEventListener('pointerup', (e) => {
|
|
1104
1113
|
// Ignore right click (on desktop it allows inspecting the chart elements without opening the URL)
|
|
1105
1114
|
if (e.button !== 0) return;
|
|
1106
1115
|
|
|
@@ -1118,33 +1127,121 @@ export default class svgMap {
|
|
|
1118
1127
|
const countryElement = e.target.closest('.svgMap-country');
|
|
1119
1128
|
if (!countryElement) return;
|
|
1120
1129
|
|
|
1121
|
-
const countryID = countryElement.
|
|
1122
|
-
const link = countryElement.
|
|
1123
|
-
const
|
|
1124
|
-
|
|
1125
|
-
|
|
1130
|
+
const countryID = countryElement.dataset.id;
|
|
1131
|
+
const link = countryElement.dataset.link;
|
|
1132
|
+
const linkTarget = countryElement.dataset.linkTarget;
|
|
1133
|
+
const hasCallback = typeof this.options.onCountryClick === 'function';
|
|
1134
|
+
const hasLink = !!link;
|
|
1126
1135
|
const isTouch = e.pointerType === 'touch' || e.pointerType === 'pen';
|
|
1127
1136
|
|
|
1137
|
+
const isClickTooltipMouse = e.pointerType === 'mouse' && isClickTooltip;
|
|
1138
|
+
|
|
1139
|
+
if (!hasLink && !hasCallback && !isClickTooltipMouse) return;
|
|
1140
|
+
|
|
1141
|
+
if (isClickTooltipMouse) {
|
|
1142
|
+
const willNavigate =
|
|
1143
|
+
hasLink && countryElement.classList.contains('svgMap-active');
|
|
1144
|
+
const shouldFireCallback = hasCallback && (!hasLink || willNavigate);
|
|
1145
|
+
|
|
1146
|
+
var callbackResultClick;
|
|
1147
|
+
if (shouldFireCallback) {
|
|
1148
|
+
callbackResultClick = this.options.onCountryClick(countryID, e);
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
if (hasLink) {
|
|
1152
|
+
if (callbackResultClick === false) return;
|
|
1153
|
+
if (countryElement.classList.contains('svgMap-active')) {
|
|
1154
|
+
if (linkTarget) window.open(link, linkTarget);
|
|
1155
|
+
else window.location.href = link;
|
|
1156
|
+
} else {
|
|
1157
|
+
clearActive();
|
|
1158
|
+
countryElement.parentNode.insertBefore(
|
|
1159
|
+
countryElement,
|
|
1160
|
+
this.persistentTooltipGroup || null
|
|
1161
|
+
);
|
|
1162
|
+
countryElement.classList.add('svgMap-active');
|
|
1163
|
+
this.setTooltipContent(this.getTooltipContent(countryID));
|
|
1164
|
+
this.showTooltip(e);
|
|
1165
|
+
}
|
|
1166
|
+
return;
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
if (callbackResultClick === false) return;
|
|
1170
|
+
|
|
1171
|
+
clearActive();
|
|
1172
|
+
countryElement.parentNode.insertBefore(
|
|
1173
|
+
countryElement,
|
|
1174
|
+
this.persistentTooltipGroup || null
|
|
1175
|
+
);
|
|
1176
|
+
countryElement.classList.add('svgMap-active');
|
|
1177
|
+
this.setTooltipContent(this.getTooltipContent(countryID));
|
|
1178
|
+
this.showTooltip(e);
|
|
1179
|
+
return;
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
const willNavigate =
|
|
1183
|
+
hasLink &&
|
|
1184
|
+
(!isTouch || countryElement.classList.contains('svgMap-active'));
|
|
1185
|
+
|
|
1186
|
+
const shouldFireCallback =
|
|
1187
|
+
hasCallback && (!hasLink || !isTouch || willNavigate);
|
|
1188
|
+
|
|
1189
|
+
var callbackResult;
|
|
1190
|
+
if (shouldFireCallback) {
|
|
1191
|
+
callbackResult = this.options.onCountryClick(countryID, e);
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
if (!hasLink) return;
|
|
1195
|
+
|
|
1196
|
+
if (callbackResult === false) return;
|
|
1197
|
+
|
|
1128
1198
|
if (isTouch) {
|
|
1129
1199
|
// Touch: only open if already active
|
|
1130
1200
|
if (countryElement.classList.contains('svgMap-active')) {
|
|
1131
|
-
if (
|
|
1201
|
+
if (linkTarget) window.open(link, linkTarget);
|
|
1132
1202
|
else window.location.href = link;
|
|
1133
1203
|
} else {
|
|
1134
|
-
// first tap shows tooltip
|
|
1204
|
+
// first tap shows tooltip (or opens link immediately if tooltips are off)
|
|
1135
1205
|
if (activeCountry) activeCountry.classList.remove('svgMap-active');
|
|
1136
1206
|
activeCountry = countryElement;
|
|
1137
1207
|
countryElement.classList.add('svgMap-active');
|
|
1138
|
-
|
|
1139
|
-
|
|
1208
|
+
if (this.options.showTooltips) {
|
|
1209
|
+
this.setTooltipContent(this.getTooltipContent(countryID));
|
|
1210
|
+
this.showTooltip(e);
|
|
1211
|
+
} else {
|
|
1212
|
+
if (linkTarget) window.open(link, linkTarget);
|
|
1213
|
+
else window.location.href = link;
|
|
1214
|
+
}
|
|
1140
1215
|
}
|
|
1141
1216
|
} else {
|
|
1142
1217
|
// Desktop: open immediately
|
|
1143
|
-
if (
|
|
1218
|
+
if (linkTarget) window.open(link, linkTarget);
|
|
1144
1219
|
else window.location.href = link;
|
|
1145
1220
|
}
|
|
1146
1221
|
});
|
|
1147
1222
|
|
|
1223
|
+
this._clickTooltipOutsideHandler = function (ev) {
|
|
1224
|
+
if (!this.tooltip || !this.tooltip.classList.contains('svgMap-active')) {
|
|
1225
|
+
return;
|
|
1226
|
+
}
|
|
1227
|
+
var node = ev.target;
|
|
1228
|
+
if (
|
|
1229
|
+
node &&
|
|
1230
|
+
node.closest &&
|
|
1231
|
+
(node.closest('.svgMap-country') || node.closest('.svgMap-tooltip'))
|
|
1232
|
+
) {
|
|
1233
|
+
return;
|
|
1234
|
+
}
|
|
1235
|
+
this.hideTooltip();
|
|
1236
|
+
if (this.mapImage) {
|
|
1237
|
+
this.mapImage
|
|
1238
|
+
.querySelectorAll('.svgMap-country.svgMap-active')
|
|
1239
|
+
.forEach(function (el) {
|
|
1240
|
+
el.classList.remove('svgMap-active');
|
|
1241
|
+
});
|
|
1242
|
+
}
|
|
1243
|
+
}.bind(this);
|
|
1244
|
+
|
|
1148
1245
|
// Expose instance
|
|
1149
1246
|
var me = this;
|
|
1150
1247
|
|
|
@@ -1210,13 +1307,82 @@ export default class svgMap {
|
|
|
1210
1307
|
}
|
|
1211
1308
|
}
|
|
1212
1309
|
|
|
1310
|
+
// Create the persistent tooltips
|
|
1311
|
+
|
|
1312
|
+
createPersistentTooltips(countryElements) {
|
|
1313
|
+
if (this.persistentTooltipGroup) {
|
|
1314
|
+
this.persistentTooltipGroup.remove();
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
this.persistentTooltipGroup = document.createElementNS(
|
|
1318
|
+
'http://www.w3.org/2000/svg',
|
|
1319
|
+
'g'
|
|
1320
|
+
);
|
|
1321
|
+
this.persistentTooltipGroup.classList.add('svgMap-persistent-tooltips');
|
|
1322
|
+
this.mapImage.appendChild(this.persistentTooltipGroup);
|
|
1323
|
+
|
|
1324
|
+
countryElements.forEach(
|
|
1325
|
+
function (countryElement) {
|
|
1326
|
+
var countryID = countryElement.dataset.id;
|
|
1327
|
+
if (!this.shouldShowTooltipOnLoad(countryID)) {
|
|
1328
|
+
return;
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
var boundingBox = countryElement.getBBox();
|
|
1332
|
+
var tooltipPosition = {
|
|
1333
|
+
x: boundingBox.x + boundingBox.width / 2,
|
|
1334
|
+
y: boundingBox.y + boundingBox.height / 2
|
|
1335
|
+
};
|
|
1336
|
+
|
|
1337
|
+
var tooltipObject = document.createElementNS(
|
|
1338
|
+
'http://www.w3.org/2000/svg',
|
|
1339
|
+
'foreignObject'
|
|
1340
|
+
);
|
|
1341
|
+
tooltipObject.setAttribute('x', tooltipPosition.x);
|
|
1342
|
+
tooltipObject.setAttribute('y', tooltipPosition.y);
|
|
1343
|
+
tooltipObject.setAttribute('width', 1);
|
|
1344
|
+
tooltipObject.setAttribute('height', 1);
|
|
1345
|
+
tooltipObject.classList.add('svgMap-persistent-tooltip-wrapper');
|
|
1346
|
+
|
|
1347
|
+
var tooltipElement = this.createElement(
|
|
1348
|
+
'div',
|
|
1349
|
+
'svgMap-persistent-tooltip',
|
|
1350
|
+
tooltipObject
|
|
1351
|
+
);
|
|
1352
|
+
tooltipElement.append(
|
|
1353
|
+
this.getTooltipContent(countryID, tooltipElement)
|
|
1354
|
+
);
|
|
1355
|
+
this.createElement('div', 'svgMap-tooltip-pointer', tooltipElement);
|
|
1356
|
+
|
|
1357
|
+
this.persistentTooltipGroup.appendChild(tooltipObject);
|
|
1358
|
+
}.bind(this)
|
|
1359
|
+
);
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
// Check if a persistent tooltip should be shown on load
|
|
1363
|
+
|
|
1364
|
+
shouldShowTooltipOnLoad(countryID) {
|
|
1365
|
+
var persistent = this.options.persistentTooltips;
|
|
1366
|
+
var countryValues = this.options.data.values[countryID];
|
|
1367
|
+
|
|
1368
|
+
if (Array.isArray(persistent)) {
|
|
1369
|
+
return persistent.indexOf(countryID) !== -1;
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
if (typeof persistent === 'function') {
|
|
1373
|
+
return persistent(countryID, countryValues);
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
return false;
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1213
1379
|
// Create the tooltip content
|
|
1214
1380
|
|
|
1215
|
-
getTooltipContent(countryID) {
|
|
1381
|
+
getTooltipContent(countryID, tooltipDiv = this.tooltip) {
|
|
1216
1382
|
// Custom tooltip
|
|
1217
1383
|
if (this.options.onGetTooltip) {
|
|
1218
1384
|
var customDiv = this.options.onGetTooltip(
|
|
1219
|
-
|
|
1385
|
+
tooltipDiv,
|
|
1220
1386
|
countryID,
|
|
1221
1387
|
this.options.data.values[countryID]
|
|
1222
1388
|
);
|
|
@@ -2198,19 +2364,50 @@ export default class svgMap {
|
|
|
2198
2364
|
// Show the tooltip
|
|
2199
2365
|
|
|
2200
2366
|
showTooltip(e) {
|
|
2367
|
+
if (!this.tooltip) {
|
|
2368
|
+
return;
|
|
2369
|
+
}
|
|
2201
2370
|
this.tooltip.classList.add('svgMap-active');
|
|
2371
|
+
|
|
2372
|
+
if (
|
|
2373
|
+
this.options.showTooltips &&
|
|
2374
|
+
this.options.tooltipTrigger === 'click' &&
|
|
2375
|
+
e.pointerType === 'mouse'
|
|
2376
|
+
) {
|
|
2377
|
+
// don't register event listener in the same frame
|
|
2378
|
+
// to prevent it from being triggered immediately
|
|
2379
|
+
requestAnimationFrame(() => {
|
|
2380
|
+
document.addEventListener(
|
|
2381
|
+
'pointerdown',
|
|
2382
|
+
this._clickTooltipOutsideHandler,
|
|
2383
|
+
{ once: true, passive: true }
|
|
2384
|
+
);
|
|
2385
|
+
});
|
|
2386
|
+
}
|
|
2387
|
+
|
|
2202
2388
|
this.moveTooltip(e);
|
|
2203
2389
|
}
|
|
2204
2390
|
|
|
2205
2391
|
// Hide the tooltip
|
|
2206
2392
|
|
|
2207
2393
|
hideTooltip() {
|
|
2394
|
+
if (!this.tooltip) {
|
|
2395
|
+
return;
|
|
2396
|
+
}
|
|
2397
|
+
|
|
2208
2398
|
this.tooltip.classList.remove('svgMap-active');
|
|
2399
|
+
document.removeEventListener(
|
|
2400
|
+
'pointerdown',
|
|
2401
|
+
this._clickTooltipOutsideHandler
|
|
2402
|
+
);
|
|
2209
2403
|
}
|
|
2210
2404
|
|
|
2211
2405
|
// Move the tooltip
|
|
2212
2406
|
|
|
2213
2407
|
moveTooltip(e) {
|
|
2408
|
+
if (!this.tooltip) {
|
|
2409
|
+
return;
|
|
2410
|
+
}
|
|
2214
2411
|
var x = e.pageX || (e.touches && e.touches[0] ? e.touches[0].pageX : null);
|
|
2215
2412
|
var y = e.pageY || (e.touches && e.touches[0] ? e.touches[0].pageY : null);
|
|
2216
2413
|
|
package/src/scss/map.scss
CHANGED
package/src/scss/tooltip.scss
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
@use 'variables' as *;
|
|
2
2
|
|
|
3
|
-
.svgMap-tooltip
|
|
3
|
+
.svgMap-tooltip,
|
|
4
|
+
.svgMap-persistent-tooltip {
|
|
4
5
|
box-shadow: 0 0 3px $mapTooltipBoxShadowColor;
|
|
5
6
|
position: absolute;
|
|
6
7
|
z-index: 2;
|
|
@@ -8,7 +9,6 @@
|
|
|
8
9
|
background: $mapTooltipBackgroundColor;
|
|
9
10
|
transform: translate(-50%, -100%);
|
|
10
11
|
border-bottom: 1px solid $mapTooltipColor;
|
|
11
|
-
display: none;
|
|
12
12
|
pointer-events: none;
|
|
13
13
|
min-width: 60px;
|
|
14
14
|
|
|
@@ -18,10 +18,6 @@
|
|
|
18
18
|
border-top: 1px solid $mapTooltipColor;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
&.svgMap-active {
|
|
22
|
-
display: block;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
21
|
.svgMap-tooltip-content-container {
|
|
26
22
|
position: relative;
|
|
27
23
|
padding: 10px 20px;
|
|
@@ -126,3 +122,27 @@
|
|
|
126
122
|
}
|
|
127
123
|
}
|
|
128
124
|
}
|
|
125
|
+
|
|
126
|
+
.svgMap-tooltip {
|
|
127
|
+
display: none;
|
|
128
|
+
|
|
129
|
+
&.svgMap-active {
|
|
130
|
+
display: block;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.svgMap-persistent-tooltips,
|
|
135
|
+
.svgMap-persistent-tooltip-wrapper {
|
|
136
|
+
pointer-events: none;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.svgMap-persistent-tooltip-wrapper {
|
|
140
|
+
overflow: visible;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.svgMap-persistent-tooltip {
|
|
144
|
+
display: block;
|
|
145
|
+
left: 0;
|
|
146
|
+
top: 0;
|
|
147
|
+
width: max-content;
|
|
148
|
+
}
|