svgmap 2.18.3 → 2.19.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/dist/index.cjs +2417 -154
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2417 -154
- package/dist/index.js.map +1 -1
- package/dist/svg-map.umd.js +4618 -2355
- package/dist/svg-map.umd.js.map +1 -1
- package/dist/svg-map.umd.min.js +1 -1
- package/dist/svgMap.js +4618 -2355
- package/dist/svgMap.js.map +1 -1
- package/dist/svgMap.min.js +1 -1
- package/package.json +4 -3
- package/src/js/core/svg-map.js +145 -154
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.19.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"keywords": [
|
|
@@ -41,11 +41,12 @@
|
|
|
41
41
|
"prepublishOnly": "npm run build && node test/assets.js"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
|
+
"@rollup/plugin-commonjs": "^29.0.0",
|
|
44
45
|
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
45
46
|
"@rollup/plugin-terser": "^0.4.4",
|
|
46
47
|
"csso": "^5.0.5",
|
|
47
|
-
"rollup": "^4.
|
|
48
|
+
"rollup": "^4.57.1",
|
|
48
49
|
"rollup-plugin-postcss": "^4.0.2",
|
|
49
|
-
"sass": "^1.97.
|
|
50
|
+
"sass": "^1.97.3"
|
|
50
51
|
}
|
|
51
52
|
}
|
package/src/js/core/svg-map.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import svgPanZoom from 'svg-pan-zoom';
|
|
2
2
|
|
|
3
3
|
export default class svgMap {
|
|
4
4
|
constructor(options = {}) {
|
|
@@ -952,56 +952,117 @@ export default class svgMap {
|
|
|
952
952
|
|
|
953
953
|
this.mapImage.appendChild(countryElement);
|
|
954
954
|
|
|
955
|
-
// Tooltip events
|
|
956
955
|
// Add tooltip when touch is used
|
|
956
|
+
function handlePointerMove(e) {
|
|
957
|
+
if (e.pointerType === 'touch') return;
|
|
958
|
+
|
|
959
|
+
const target = document.elementFromPoint(e.clientX, e.clientY);
|
|
960
|
+
|
|
961
|
+
if (
|
|
962
|
+
!target ||
|
|
963
|
+
(!target.closest('.svgMap-country') &&
|
|
964
|
+
!target.closest('.svgMap-tooltip'))
|
|
965
|
+
) {
|
|
966
|
+
this.hideTooltip();
|
|
967
|
+
document
|
|
968
|
+
.querySelectorAll('.svgMap-active')
|
|
969
|
+
.forEach(el => el.classList.remove('svgMap-active'));
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
const handlePointerMoveBound = handlePointerMove.bind(this);
|
|
974
|
+
|
|
957
975
|
countryElement.addEventListener(
|
|
958
|
-
'
|
|
976
|
+
'pointerenter',
|
|
959
977
|
function (e) {
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
978
|
+
// Only add pointermove listener for non-touch pointers
|
|
979
|
+
if (e.pointerType !== 'touch') {
|
|
980
|
+
document.addEventListener(
|
|
981
|
+
'pointermove',
|
|
982
|
+
handlePointerMoveBound,
|
|
983
|
+
{ passive: true }
|
|
984
|
+
);
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
document
|
|
988
|
+
.querySelectorAll('.svgMap-active')
|
|
989
|
+
.forEach(el => el.classList.remove('svgMap-active'));
|
|
990
|
+
|
|
964
991
|
countryElement.parentNode.appendChild(countryElement);
|
|
965
992
|
countryElement.classList.add('svgMap-active');
|
|
966
993
|
|
|
967
|
-
|
|
968
|
-
var countryLink = countryElement.getAttribute('data-link');
|
|
969
|
-
if (this.options.touchLink) {
|
|
970
|
-
if (countryLink) {
|
|
971
|
-
window.location.href = countryLink;
|
|
972
|
-
return;
|
|
973
|
-
}
|
|
974
|
-
}
|
|
994
|
+
const countryID = countryElement.getAttribute('data-id');
|
|
975
995
|
this.setTooltipContent(this.getTooltipContent(countryID));
|
|
976
996
|
this.showTooltip(e);
|
|
997
|
+
|
|
998
|
+
// For touch, move tooltip to the touch position and keep it there
|
|
999
|
+
if (e.pointerType === 'touch') {
|
|
1000
|
+
this.moveTooltip(e);
|
|
1001
|
+
}
|
|
1002
|
+
}.bind(this)
|
|
1003
|
+
);
|
|
1004
|
+
|
|
1005
|
+
// Handle touch move - update tooltip position while panning
|
|
1006
|
+
countryElement.addEventListener(
|
|
1007
|
+
'touchmove',
|
|
1008
|
+
function (e) {
|
|
977
1009
|
this.moveTooltip(e);
|
|
1010
|
+
}.bind(this),
|
|
1011
|
+
{ passive: true }
|
|
1012
|
+
);
|
|
978
1013
|
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
1014
|
+
// Handle touch end - remove active state and hide tooltip
|
|
1015
|
+
countryElement.addEventListener(
|
|
1016
|
+
'touchend',
|
|
1017
|
+
function (e) {
|
|
1018
|
+
const touch = e.changedTouches[0];
|
|
1019
|
+
const elementAtEnd = document.elementFromPoint(touch.clientX, touch.clientY);
|
|
1020
|
+
|
|
1021
|
+
// Only hide if touch ended outside the country or tooltip
|
|
1022
|
+
if (
|
|
1023
|
+
!elementAtEnd ||
|
|
1024
|
+
(!elementAtEnd.closest('.svgMap-country') &&
|
|
1025
|
+
!elementAtEnd.closest('.svgMap-tooltip'))
|
|
1026
|
+
) {
|
|
1027
|
+
this.hideTooltip();
|
|
1028
|
+
document
|
|
1029
|
+
.querySelectorAll('.svgMap-active')
|
|
1030
|
+
.forEach(el => el.classList.remove('svgMap-active'));
|
|
1031
|
+
}
|
|
986
1032
|
}.bind(this),
|
|
987
1033
|
{ passive: true }
|
|
988
1034
|
);
|
|
989
1035
|
|
|
1036
|
+
// Remove pointermove listener when leaving non-touch pointer
|
|
990
1037
|
countryElement.addEventListener(
|
|
991
|
-
'
|
|
1038
|
+
'pointerleave',
|
|
992
1039
|
function (e) {
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1040
|
+
if (e.pointerType !== 'touch') {
|
|
1041
|
+
document.removeEventListener('pointermove', handlePointerMoveBound);
|
|
1042
|
+
this.hideTooltip();
|
|
1043
|
+
document
|
|
1044
|
+
.querySelectorAll('.svgMap-active')
|
|
1045
|
+
.forEach(el => el.classList.remove('svgMap-active'));
|
|
1046
|
+
}
|
|
1047
|
+
}.bind(this)
|
|
1048
|
+
);
|
|
1049
|
+
|
|
1050
|
+
document.addEventListener(
|
|
1051
|
+
'pointerover',
|
|
1052
|
+
function (e) {
|
|
1053
|
+
if (e.pointerType !== 'touch') return;
|
|
1054
|
+
|
|
1055
|
+
if (
|
|
1056
|
+
e.target.closest('.svgMap-country') ||
|
|
1057
|
+
e.target.closest('.svgMap-tooltip')
|
|
1058
|
+
) {
|
|
1059
|
+
return;
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
this.hideTooltip();
|
|
1063
|
+
document
|
|
1064
|
+
.querySelectorAll('.svgMap-active')
|
|
1065
|
+
.forEach(el => el.classList.remove('svgMap-active'));
|
|
1005
1066
|
}.bind(this),
|
|
1006
1067
|
{ passive: true }
|
|
1007
1068
|
);
|
|
@@ -1021,140 +1082,70 @@ export default class svgMap {
|
|
|
1021
1082
|
this.options.data.values[countryID]['linkTarget']
|
|
1022
1083
|
);
|
|
1023
1084
|
}
|
|
1085
|
+
}
|
|
1086
|
+
}.bind(this)
|
|
1087
|
+
);
|
|
1024
1088
|
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
dragged = false;
|
|
1028
|
-
});
|
|
1029
|
-
countryElement.addEventListener('touchstart', function () {
|
|
1030
|
-
dragged = false;
|
|
1031
|
-
});
|
|
1032
|
-
countryElement.addEventListener('mousemove', function () {
|
|
1033
|
-
dragged = true;
|
|
1034
|
-
});
|
|
1035
|
-
countryElement.addEventListener('touchmove', function () {
|
|
1036
|
-
dragged = true;
|
|
1037
|
-
});
|
|
1038
|
-
const clickHandler = function (e) {
|
|
1039
|
-
if (dragged) {
|
|
1040
|
-
return;
|
|
1041
|
-
}
|
|
1089
|
+
let pointerStart = null;
|
|
1090
|
+
let activeCountry = null;
|
|
1042
1091
|
|
|
1043
|
-
|
|
1044
|
-
|
|
1092
|
+
this.mapImage.addEventListener('pointerdown', e => {
|
|
1093
|
+
// Ignore right click (on desktop it allows inspecting the chart elements without opening the URL)
|
|
1094
|
+
if (e.button !== 0) return;
|
|
1045
1095
|
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
} else {
|
|
1049
|
-
window.location.href = link;
|
|
1050
|
-
}
|
|
1051
|
-
};
|
|
1096
|
+
pointerStart = { x: e.clientX, y: e.clientY };
|
|
1097
|
+
}, { passive: true });
|
|
1052
1098
|
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1099
|
+
this.mapImage.addEventListener('pointerup', e => {
|
|
1100
|
+
// Ignore right click (on desktop it allows inspecting the chart elements without opening the URL)
|
|
1101
|
+
if (e.button !== 0) return;
|
|
1056
1102
|
|
|
1057
|
-
|
|
1058
|
-
countryElement.addEventListener(
|
|
1059
|
-
'mouseleave',
|
|
1060
|
-
function () {
|
|
1061
|
-
this.hideTooltip();
|
|
1062
|
-
countryElement.classList.remove('svgMap-active');
|
|
1063
|
-
countryElement.removeEventListener(
|
|
1064
|
-
'mousemove',
|
|
1065
|
-
this.tooltipMoveEvent,
|
|
1066
|
-
{
|
|
1067
|
-
passive: true
|
|
1068
|
-
}
|
|
1069
|
-
);
|
|
1070
|
-
}.bind(this),
|
|
1071
|
-
{ passive: true }
|
|
1072
|
-
);
|
|
1103
|
+
if (!pointerStart) return;
|
|
1073
1104
|
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
// Do not hide the tooltip, so it stays open on mobile
|
|
1079
|
-
}.bind(this),
|
|
1080
|
-
{ passive: true }
|
|
1081
|
-
);
|
|
1105
|
+
const dragThreshold = 5; // pixels
|
|
1106
|
+
const moved =
|
|
1107
|
+
Math.abs(pointerStart.x - e.clientX) > dragThreshold ||
|
|
1108
|
+
Math.abs(pointerStart.y - e.clientY) > dragThreshold;
|
|
1082
1109
|
|
|
1083
|
-
|
|
1084
|
-
countryElement.addEventListener(
|
|
1085
|
-
'click',
|
|
1086
|
-
function (e) {
|
|
1087
|
-
e.stopPropagation();
|
|
1088
|
-
var countryID = countryElement.getAttribute('data-id');
|
|
1089
|
-
if (countryElement.getAttribute('data-tooltip-open') === 'true') {
|
|
1090
|
-
this.hideTooltip();
|
|
1091
|
-
countryElement.setAttribute('data-tooltip-open', 'false');
|
|
1092
|
-
} else {
|
|
1093
|
-
this.setTooltipContent(this.getTooltipContent(countryID));
|
|
1094
|
-
this.showTooltip(e);
|
|
1095
|
-
countryElement.setAttribute('data-tooltip-open', 'true');
|
|
1096
|
-
}
|
|
1097
|
-
}.bind(this),
|
|
1098
|
-
{ passive: true }
|
|
1099
|
-
);
|
|
1100
|
-
}.bind(this)
|
|
1101
|
-
);
|
|
1110
|
+
pointerStart = null;
|
|
1102
1111
|
|
|
1103
|
-
|
|
1104
|
-
document.addEventListener(
|
|
1105
|
-
'touchend',
|
|
1106
|
-
function (e) {
|
|
1107
|
-
if (
|
|
1108
|
-
e.target.closest('.svgMap-country') ||
|
|
1109
|
-
e.target.closest('.svgMap-tooltip')
|
|
1110
|
-
) {
|
|
1111
|
-
return;
|
|
1112
|
-
}
|
|
1113
|
-
this.hideTooltip();
|
|
1114
|
-
var openTooltips = document.querySelectorAll(
|
|
1115
|
-
'[data-tooltip-open="true"]'
|
|
1116
|
-
);
|
|
1117
|
-
openTooltips.forEach(function (element) {
|
|
1118
|
-
element.setAttribute('data-tooltip-open', 'false');
|
|
1119
|
-
});
|
|
1120
|
-
var activeCountries = document.querySelectorAll('.svgMap-active');
|
|
1121
|
-
activeCountries.forEach(function (element) {
|
|
1122
|
-
element.classList.remove('svgMap-active');
|
|
1123
|
-
});
|
|
1124
|
-
}.bind(this),
|
|
1125
|
-
{ passive: true }
|
|
1126
|
-
);
|
|
1112
|
+
if (moved) return;
|
|
1127
1113
|
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1114
|
+
const countryElement = e.target.closest('.svgMap-country');
|
|
1115
|
+
if (!countryElement) return;
|
|
1116
|
+
|
|
1117
|
+
const countryID = countryElement.getAttribute('data-id');
|
|
1118
|
+
const link = countryElement.getAttribute('data-link');
|
|
1119
|
+
const target = countryElement.getAttribute('data-link-target');
|
|
1120
|
+
if (!link) return;
|
|
1121
|
+
|
|
1122
|
+
const isTouch = e.pointerType === 'touch' || e.pointerType === 'pen';
|
|
1123
|
+
|
|
1124
|
+
if (isTouch) {
|
|
1125
|
+
// Touch: only open if already active
|
|
1126
|
+
if (countryElement.classList.contains('svgMap-active')) {
|
|
1127
|
+
if (target) window.open(link, target);
|
|
1128
|
+
else window.location.href = link;
|
|
1129
|
+
} else {
|
|
1130
|
+
// first tap shows tooltip
|
|
1131
|
+
if (activeCountry) activeCountry.classList.remove('svgMap-active');
|
|
1132
|
+
activeCountry = countryElement;
|
|
1133
|
+
countryElement.classList.add('svgMap-active');
|
|
1134
|
+
this.setTooltipContent(this.getTooltipContent(countryID));
|
|
1135
|
+
this.showTooltip(e);
|
|
1137
1136
|
}
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
});
|
|
1145
|
-
var activeCountries = document.querySelectorAll('.svgMap-active');
|
|
1146
|
-
activeCountries.forEach(function (element) {
|
|
1147
|
-
element.classList.remove('svgMap-active');
|
|
1148
|
-
});
|
|
1149
|
-
}.bind(this),
|
|
1150
|
-
{ passive: true }
|
|
1151
|
-
);
|
|
1137
|
+
} else {
|
|
1138
|
+
// Desktop: open immediately
|
|
1139
|
+
if (target) window.open(link, target);
|
|
1140
|
+
else window.location.href = link;
|
|
1141
|
+
}
|
|
1142
|
+
});
|
|
1152
1143
|
|
|
1153
1144
|
// Expose instance
|
|
1154
1145
|
var me = this;
|
|
1155
1146
|
|
|
1156
1147
|
// Init pan zoom
|
|
1157
|
-
this.mapPanZoom = svgPanZoom
|
|
1148
|
+
this.mapPanZoom = svgPanZoom(this.mapImage, {
|
|
1158
1149
|
zoomEnabled: this.options.allowInteraction,
|
|
1159
1150
|
panEnabled: this.options.allowInteraction,
|
|
1160
1151
|
fit: true,
|