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/README.md
CHANGED
|
@@ -99,7 +99,11 @@ You can pass the following options into svgMap:
|
|
|
99
99
|
| `hideFlag` | `boolean` | `false` | Hide the flag in tooltips |
|
|
100
100
|
| `noDataText` | `string` | `'No data available'` | The text to be shown when no data is present |
|
|
101
101
|
| `touchLink` | `boolean` | `false` | Set to `true` to open the link (see `data.values.link`) on mobile devices, by default the tooltip will be shown |
|
|
102
|
+
| `showTooltips` | `boolean` | `true` | When `false`, disables hover and touch-following tooltips only. Persistent on-map labels from `persistentTooltips` are unaffected. On touch devices, countries with a `link` open it on the first tap instead of using the two-tap pattern (first tap preview, second tap navigate). |
|
|
103
|
+
| `tooltipTrigger` | `'hover'`, `'click'` | `'hover'` | How the floating tooltip opens with the **mouse**: `'hover'` opens on mouseenter/mouseleave.`'click'` opens on primary click and closes when clicking outside the map countries or tooltip. Only applies when `showTooltips` is `true`. |
|
|
104
|
+
| `persistentTooltips` | `false`, `array`, `function` | `false` | Persistent tooltips fixed on the map when it loads: an array of country IDs, or a function (`function (countryID, countryValues) { … }`) to decide per country. Independent of `showTooltips`. Best used with `showTooltips: false` or `tooltipTrigger: 'click'`. |
|
|
102
105
|
| `onGetTooltip` | `function` | | Called when a tooltip is created to custimize the tooltip content (`function (tooltipDiv, countryID, countryValues) { return 'Custom HTML'; }`) |
|
|
106
|
+
| `onCountryClick` | `function` | | Called when the user clicks a country (primary button, pointer released without dragging). Signature: `function (countryID, event) { … }`. Use this for custom actions instead of or in addition to `data.values.link`. Return `false` to skip opening the URL when the country has a `link`. On touch devices with a link, the callback runs when the tap would navigate (not on the first tap that only shows the tooltip). Countries show a pointer cursor while this option is set. |
|
|
103
107
|
| `countries` | `object` | | Additional options specific to countries: |
|
|
104
108
|
| `↳ EH` | `boolean` | `true` | When set to `false`, Western Sahara (EH) will be combined with Morocco (MA) |
|
|
105
109
|
| `data` | `object` | | The chart data to use for coloring and to show in the tooltip. Use a unique data-id as key and provide following options as value: |
|
package/dist/index.cjs
CHANGED
|
@@ -2344,6 +2344,15 @@ class svgMap {
|
|
|
2344
2344
|
// Set to true to open the link on mobile devices, set to false (default) to show the tooltip
|
|
2345
2345
|
touchLink: false,
|
|
2346
2346
|
|
|
2347
|
+
// When false, disables hover/touch-following tooltips (not the on-map persistent labels; see persistentTooltips)
|
|
2348
|
+
showTooltips: true,
|
|
2349
|
+
|
|
2350
|
+
// 'hover' (default): mouse shows tooltip on enter. 'click': mouse opens tooltip on click; touch/pen unchanged.
|
|
2351
|
+
tooltipTrigger: 'hover',
|
|
2352
|
+
|
|
2353
|
+
// Persistent on-map tooltips: an array of country IDs, or a function (countryID, countryValues) => boolean
|
|
2354
|
+
persistentTooltips: false,
|
|
2355
|
+
|
|
2347
2356
|
// Set to true to show the to show a zoom reset button
|
|
2348
2357
|
showZoomReset: false,
|
|
2349
2358
|
|
|
@@ -2352,6 +2361,10 @@ class svgMap {
|
|
|
2352
2361
|
return null;
|
|
2353
2362
|
},
|
|
2354
2363
|
|
|
2364
|
+
// Called on country click (pointer released without dragging). Receives
|
|
2365
|
+
// (countryID, event). Return false to skip opening data.values[*].link.
|
|
2366
|
+
onCountryClick: null,
|
|
2367
|
+
|
|
2355
2368
|
// Country specific options
|
|
2356
2369
|
countries: {
|
|
2357
2370
|
// Western Sahara: Set to false to combine Morocco (MA) and Western Sahara (EH)
|
|
@@ -2391,6 +2404,9 @@ class svgMap {
|
|
|
2391
2404
|
// Wrapper element
|
|
2392
2405
|
this.wrapper = document.getElementById(this.options.targetElementID);
|
|
2393
2406
|
this.wrapper.classList.add('svgMap-wrapper');
|
|
2407
|
+
if (typeof this.options.onCountryClick === 'function') {
|
|
2408
|
+
this.wrapper.classList.add('svgMap-country-click-callback');
|
|
2409
|
+
}
|
|
2394
2410
|
|
|
2395
2411
|
// Container element
|
|
2396
2412
|
this.container = document.createElement('div');
|
|
@@ -3082,7 +3098,9 @@ class svgMap {
|
|
|
3082
3098
|
|
|
3083
3099
|
createMap() {
|
|
3084
3100
|
// Create the tooltip
|
|
3085
|
-
this.
|
|
3101
|
+
if (this.options.showTooltips) {
|
|
3102
|
+
this.createTooltip();
|
|
3103
|
+
}
|
|
3086
3104
|
|
|
3087
3105
|
// Create map wrappers
|
|
3088
3106
|
this.mapWrapper = this.createElement(
|
|
@@ -3208,6 +3226,103 @@ class svgMap {
|
|
|
3208
3226
|
}.bind(this);
|
|
3209
3227
|
|
|
3210
3228
|
// Add map elements
|
|
3229
|
+
var countryElements = [];
|
|
3230
|
+
|
|
3231
|
+
const clearActive = function clearActive() {
|
|
3232
|
+
this.mapImage
|
|
3233
|
+
.querySelectorAll('.svgMap-active')
|
|
3234
|
+
.forEach((el) => el.classList.remove('svgMap-active'));
|
|
3235
|
+
}.bind(this);
|
|
3236
|
+
|
|
3237
|
+
const isClickTooltip =
|
|
3238
|
+
this.options.showTooltips && this.options.tooltipTrigger === 'click';
|
|
3239
|
+
|
|
3240
|
+
const getCountryFromEvent = function (e) {
|
|
3241
|
+
return e.target && e.target.closest
|
|
3242
|
+
? e.target.closest('.svgMap-country')
|
|
3243
|
+
: null;
|
|
3244
|
+
};
|
|
3245
|
+
|
|
3246
|
+
const raiseCountry = function (countryElement, setActive) {
|
|
3247
|
+
if (setActive) {
|
|
3248
|
+
clearActive();
|
|
3249
|
+
}
|
|
3250
|
+
countryElement.parentNode.insertBefore(
|
|
3251
|
+
countryElement,
|
|
3252
|
+
this.persistentTooltipGroup || null
|
|
3253
|
+
);
|
|
3254
|
+
if (setActive) {
|
|
3255
|
+
countryElement.classList.add('svgMap-active');
|
|
3256
|
+
}
|
|
3257
|
+
}.bind(this);
|
|
3258
|
+
|
|
3259
|
+
const showCountryTooltip = function (countryElement, e, setActive) {
|
|
3260
|
+
raiseCountry(countryElement, setActive);
|
|
3261
|
+
this.setTooltipContent(this.getTooltipContent(countryElement.dataset.id));
|
|
3262
|
+
this.showTooltip(e);
|
|
3263
|
+
}.bind(this);
|
|
3264
|
+
|
|
3265
|
+
// Touch only: preview tooltip on finger down without marking country active
|
|
3266
|
+
// (active is set on pointerup so link countries keep two-tap navigation)
|
|
3267
|
+
this.mapImage.addEventListener(
|
|
3268
|
+
'pointerdown',
|
|
3269
|
+
(e) => {
|
|
3270
|
+
if (!this.options.showTooltips || e.pointerType !== 'touch') {
|
|
3271
|
+
return;
|
|
3272
|
+
}
|
|
3273
|
+
|
|
3274
|
+
const countryElement = getCountryFromEvent(e);
|
|
3275
|
+
if (!countryElement) {
|
|
3276
|
+
this.hideTooltip();
|
|
3277
|
+
return;
|
|
3278
|
+
}
|
|
3279
|
+
|
|
3280
|
+
showCountryTooltip(countryElement, e, false);
|
|
3281
|
+
this.moveTooltip(e);
|
|
3282
|
+
},
|
|
3283
|
+
{ passive: true }
|
|
3284
|
+
);
|
|
3285
|
+
|
|
3286
|
+
this.mapImage.addEventListener(
|
|
3287
|
+
'pointercancel',
|
|
3288
|
+
(e) => {
|
|
3289
|
+
if (e.pointerType === 'touch') {
|
|
3290
|
+
this.hideTooltip();
|
|
3291
|
+
}
|
|
3292
|
+
},
|
|
3293
|
+
{ passive: true }
|
|
3294
|
+
);
|
|
3295
|
+
|
|
3296
|
+
// Hover (mouse/pen) and touch drag: raise country + optional floating tooltip
|
|
3297
|
+
this.mapImage.addEventListener(
|
|
3298
|
+
'pointermove',
|
|
3299
|
+
(e) => {
|
|
3300
|
+
const countryElement = getCountryFromEvent(e);
|
|
3301
|
+
if (!countryElement) {
|
|
3302
|
+
clearActive();
|
|
3303
|
+
if (this.options.showTooltips) {
|
|
3304
|
+
this.hideTooltip();
|
|
3305
|
+
}
|
|
3306
|
+
return;
|
|
3307
|
+
}
|
|
3308
|
+
|
|
3309
|
+
const mouseClickMode = e.pointerType === 'mouse' && isClickTooltip;
|
|
3310
|
+
|
|
3311
|
+
// Always raise hovered country (SVG paint order + .svgMap-active stroke)
|
|
3312
|
+
if (!this.options.showTooltips || mouseClickMode) {
|
|
3313
|
+
raiseCountry(countryElement, true);
|
|
3314
|
+
return;
|
|
3315
|
+
}
|
|
3316
|
+
|
|
3317
|
+
showCountryTooltip(countryElement, e, true);
|
|
3318
|
+
|
|
3319
|
+
if (e.pointerType === 'touch') {
|
|
3320
|
+
this.moveTooltip(e);
|
|
3321
|
+
}
|
|
3322
|
+
},
|
|
3323
|
+
{ passive: true }
|
|
3324
|
+
);
|
|
3325
|
+
|
|
3211
3326
|
Object.keys(mapPaths).forEach(
|
|
3212
3327
|
function (countryID) {
|
|
3213
3328
|
var countryData = this.mapPaths[countryID];
|
|
@@ -3225,156 +3340,50 @@ class svgMap {
|
|
|
3225
3340
|
'id',
|
|
3226
3341
|
this.id + '-map-country-' + countryID
|
|
3227
3342
|
);
|
|
3228
|
-
countryElement.
|
|
3343
|
+
countryElement.dataset.id = countryID;
|
|
3229
3344
|
countryElement.classList.add('svgMap-country');
|
|
3230
3345
|
|
|
3231
3346
|
this.mapImage.appendChild(countryElement);
|
|
3232
|
-
|
|
3233
|
-
// Add tooltip when touch is used
|
|
3234
|
-
function handlePointerMove(e) {
|
|
3235
|
-
if (e.pointerType === 'touch') return;
|
|
3236
|
-
|
|
3237
|
-
const target = document.elementFromPoint(e.clientX, e.clientY);
|
|
3238
|
-
|
|
3239
|
-
if (
|
|
3240
|
-
!target ||
|
|
3241
|
-
(!target.closest('.svgMap-country') &&
|
|
3242
|
-
!target.closest('.svgMap-tooltip'))
|
|
3243
|
-
) {
|
|
3244
|
-
this.hideTooltip();
|
|
3245
|
-
document
|
|
3246
|
-
.querySelectorAll('.svgMap-active')
|
|
3247
|
-
.forEach(el => el.classList.remove('svgMap-active'));
|
|
3248
|
-
}
|
|
3249
|
-
}
|
|
3250
|
-
|
|
3251
|
-
const handlePointerMoveBound = handlePointerMove.bind(this);
|
|
3252
|
-
|
|
3253
|
-
countryElement.addEventListener(
|
|
3254
|
-
'pointerenter',
|
|
3255
|
-
function (e) {
|
|
3256
|
-
// Only add pointermove listener for non-touch pointers
|
|
3257
|
-
if (e.pointerType !== 'touch') {
|
|
3258
|
-
document.addEventListener(
|
|
3259
|
-
'pointermove',
|
|
3260
|
-
handlePointerMoveBound,
|
|
3261
|
-
{ passive: true }
|
|
3262
|
-
);
|
|
3263
|
-
}
|
|
3264
|
-
|
|
3265
|
-
document
|
|
3266
|
-
.querySelectorAll('.svgMap-active')
|
|
3267
|
-
.forEach(el => el.classList.remove('svgMap-active'));
|
|
3268
|
-
|
|
3269
|
-
countryElement.parentNode.appendChild(countryElement);
|
|
3270
|
-
countryElement.classList.add('svgMap-active');
|
|
3271
|
-
|
|
3272
|
-
const countryID = countryElement.getAttribute('data-id');
|
|
3273
|
-
this.setTooltipContent(this.getTooltipContent(countryID));
|
|
3274
|
-
this.showTooltip(e);
|
|
3275
|
-
|
|
3276
|
-
// For touch, move tooltip to the touch position and keep it there
|
|
3277
|
-
if (e.pointerType === 'touch') {
|
|
3278
|
-
this.moveTooltip(e);
|
|
3279
|
-
}
|
|
3280
|
-
}.bind(this)
|
|
3281
|
-
);
|
|
3282
|
-
|
|
3283
|
-
// Handle touch move - update tooltip position while panning
|
|
3284
|
-
countryElement.addEventListener(
|
|
3285
|
-
'touchmove',
|
|
3286
|
-
function (e) {
|
|
3287
|
-
this.moveTooltip(e);
|
|
3288
|
-
}.bind(this),
|
|
3289
|
-
{ passive: true }
|
|
3290
|
-
);
|
|
3291
|
-
|
|
3292
|
-
// Handle touch end - remove active state and hide tooltip
|
|
3293
|
-
countryElement.addEventListener(
|
|
3294
|
-
'touchend',
|
|
3295
|
-
function (e) {
|
|
3296
|
-
const touch = e.changedTouches[0];
|
|
3297
|
-
const elementAtEnd = document.elementFromPoint(touch.clientX, touch.clientY);
|
|
3298
|
-
|
|
3299
|
-
// Only hide if touch ended outside the country or tooltip
|
|
3300
|
-
if (
|
|
3301
|
-
!elementAtEnd ||
|
|
3302
|
-
(!elementAtEnd.closest('.svgMap-country') &&
|
|
3303
|
-
!elementAtEnd.closest('.svgMap-tooltip'))
|
|
3304
|
-
) {
|
|
3305
|
-
this.hideTooltip();
|
|
3306
|
-
document
|
|
3307
|
-
.querySelectorAll('.svgMap-active')
|
|
3308
|
-
.forEach(el => el.classList.remove('svgMap-active'));
|
|
3309
|
-
}
|
|
3310
|
-
}.bind(this),
|
|
3311
|
-
{ passive: true }
|
|
3312
|
-
);
|
|
3313
|
-
|
|
3314
|
-
// Remove pointermove listener when leaving non-touch pointer
|
|
3315
|
-
countryElement.addEventListener(
|
|
3316
|
-
'pointerleave',
|
|
3317
|
-
function (e) {
|
|
3318
|
-
if (e.pointerType !== 'touch') {
|
|
3319
|
-
document.removeEventListener('pointermove', handlePointerMoveBound);
|
|
3320
|
-
this.hideTooltip();
|
|
3321
|
-
document
|
|
3322
|
-
.querySelectorAll('.svgMap-active')
|
|
3323
|
-
.forEach(el => el.classList.remove('svgMap-active'));
|
|
3324
|
-
}
|
|
3325
|
-
}.bind(this)
|
|
3326
|
-
);
|
|
3327
|
-
|
|
3328
|
-
document.addEventListener(
|
|
3329
|
-
'pointerover',
|
|
3330
|
-
function (e) {
|
|
3331
|
-
if (e.pointerType !== 'touch') return;
|
|
3332
|
-
|
|
3333
|
-
if (
|
|
3334
|
-
e.target.closest('.svgMap-country') ||
|
|
3335
|
-
e.target.closest('.svgMap-tooltip')
|
|
3336
|
-
) {
|
|
3337
|
-
return;
|
|
3338
|
-
}
|
|
3339
|
-
|
|
3340
|
-
this.hideTooltip();
|
|
3341
|
-
document
|
|
3342
|
-
.querySelectorAll('.svgMap-active')
|
|
3343
|
-
.forEach(el => el.classList.remove('svgMap-active'));
|
|
3344
|
-
}.bind(this),
|
|
3345
|
-
{ passive: true }
|
|
3346
|
-
);
|
|
3347
|
+
countryElements.push(countryElement);
|
|
3347
3348
|
|
|
3348
3349
|
if (
|
|
3349
3350
|
this.options.data.values &&
|
|
3350
3351
|
this.options.data.values[countryID] &&
|
|
3351
3352
|
this.options.data.values[countryID]['link']
|
|
3352
3353
|
) {
|
|
3353
|
-
countryElement.
|
|
3354
|
-
'
|
|
3355
|
-
this.options.data.values[countryID]['link']
|
|
3356
|
-
);
|
|
3354
|
+
countryElement.dataset.link =
|
|
3355
|
+
this.options.data.values[countryID]['link'];
|
|
3357
3356
|
if (this.options.data.values[countryID]['linkTarget']) {
|
|
3358
|
-
countryElement.
|
|
3359
|
-
|
|
3360
|
-
this.options.data.values[countryID]['linkTarget']
|
|
3361
|
-
);
|
|
3357
|
+
countryElement.dataset.linkTarget =
|
|
3358
|
+
this.options.data.values[countryID]['linkTarget'];
|
|
3362
3359
|
}
|
|
3363
3360
|
}
|
|
3364
3361
|
}.bind(this)
|
|
3365
3362
|
);
|
|
3366
3363
|
|
|
3364
|
+
var persistent = this.options.persistentTooltips;
|
|
3365
|
+
if (
|
|
3366
|
+
persistent &&
|
|
3367
|
+
(Array.isArray(persistent) || typeof persistent === 'function')
|
|
3368
|
+
) {
|
|
3369
|
+
this.createPersistentTooltips(countryElements);
|
|
3370
|
+
}
|
|
3371
|
+
|
|
3367
3372
|
let pointerStart = null;
|
|
3368
3373
|
let activeCountry = null;
|
|
3369
3374
|
|
|
3370
|
-
this.mapImage.addEventListener(
|
|
3371
|
-
|
|
3372
|
-
|
|
3375
|
+
this.mapImage.addEventListener(
|
|
3376
|
+
'pointerdown',
|
|
3377
|
+
(e) => {
|
|
3378
|
+
// Ignore right click (on desktop it allows inspecting the chart elements without opening the URL)
|
|
3379
|
+
if (e.button !== 0) return;
|
|
3373
3380
|
|
|
3374
|
-
|
|
3375
|
-
|
|
3381
|
+
pointerStart = { x: e.clientX, y: e.clientY };
|
|
3382
|
+
},
|
|
3383
|
+
{ passive: true }
|
|
3384
|
+
);
|
|
3376
3385
|
|
|
3377
|
-
this.mapImage.addEventListener('pointerup', e => {
|
|
3386
|
+
this.mapImage.addEventListener('pointerup', (e) => {
|
|
3378
3387
|
// Ignore right click (on desktop it allows inspecting the chart elements without opening the URL)
|
|
3379
3388
|
if (e.button !== 0) return;
|
|
3380
3389
|
|
|
@@ -3392,33 +3401,121 @@ class svgMap {
|
|
|
3392
3401
|
const countryElement = e.target.closest('.svgMap-country');
|
|
3393
3402
|
if (!countryElement) return;
|
|
3394
3403
|
|
|
3395
|
-
const countryID = countryElement.
|
|
3396
|
-
const link = countryElement.
|
|
3397
|
-
const
|
|
3398
|
-
|
|
3399
|
-
|
|
3404
|
+
const countryID = countryElement.dataset.id;
|
|
3405
|
+
const link = countryElement.dataset.link;
|
|
3406
|
+
const linkTarget = countryElement.dataset.linkTarget;
|
|
3407
|
+
const hasCallback = typeof this.options.onCountryClick === 'function';
|
|
3408
|
+
const hasLink = !!link;
|
|
3400
3409
|
const isTouch = e.pointerType === 'touch' || e.pointerType === 'pen';
|
|
3401
3410
|
|
|
3411
|
+
const isClickTooltipMouse = e.pointerType === 'mouse' && isClickTooltip;
|
|
3412
|
+
|
|
3413
|
+
if (!hasLink && !hasCallback && !isClickTooltipMouse) return;
|
|
3414
|
+
|
|
3415
|
+
if (isClickTooltipMouse) {
|
|
3416
|
+
const willNavigate =
|
|
3417
|
+
hasLink && countryElement.classList.contains('svgMap-active');
|
|
3418
|
+
const shouldFireCallback = hasCallback && (!hasLink || willNavigate);
|
|
3419
|
+
|
|
3420
|
+
var callbackResultClick;
|
|
3421
|
+
if (shouldFireCallback) {
|
|
3422
|
+
callbackResultClick = this.options.onCountryClick(countryID, e);
|
|
3423
|
+
}
|
|
3424
|
+
|
|
3425
|
+
if (hasLink) {
|
|
3426
|
+
if (callbackResultClick === false) return;
|
|
3427
|
+
if (countryElement.classList.contains('svgMap-active')) {
|
|
3428
|
+
if (linkTarget) window.open(link, linkTarget);
|
|
3429
|
+
else window.location.href = link;
|
|
3430
|
+
} else {
|
|
3431
|
+
clearActive();
|
|
3432
|
+
countryElement.parentNode.insertBefore(
|
|
3433
|
+
countryElement,
|
|
3434
|
+
this.persistentTooltipGroup || null
|
|
3435
|
+
);
|
|
3436
|
+
countryElement.classList.add('svgMap-active');
|
|
3437
|
+
this.setTooltipContent(this.getTooltipContent(countryID));
|
|
3438
|
+
this.showTooltip(e);
|
|
3439
|
+
}
|
|
3440
|
+
return;
|
|
3441
|
+
}
|
|
3442
|
+
|
|
3443
|
+
if (callbackResultClick === false) return;
|
|
3444
|
+
|
|
3445
|
+
clearActive();
|
|
3446
|
+
countryElement.parentNode.insertBefore(
|
|
3447
|
+
countryElement,
|
|
3448
|
+
this.persistentTooltipGroup || null
|
|
3449
|
+
);
|
|
3450
|
+
countryElement.classList.add('svgMap-active');
|
|
3451
|
+
this.setTooltipContent(this.getTooltipContent(countryID));
|
|
3452
|
+
this.showTooltip(e);
|
|
3453
|
+
return;
|
|
3454
|
+
}
|
|
3455
|
+
|
|
3456
|
+
const willNavigate =
|
|
3457
|
+
hasLink &&
|
|
3458
|
+
(!isTouch || countryElement.classList.contains('svgMap-active'));
|
|
3459
|
+
|
|
3460
|
+
const shouldFireCallback =
|
|
3461
|
+
hasCallback && (!hasLink || !isTouch || willNavigate);
|
|
3462
|
+
|
|
3463
|
+
var callbackResult;
|
|
3464
|
+
if (shouldFireCallback) {
|
|
3465
|
+
callbackResult = this.options.onCountryClick(countryID, e);
|
|
3466
|
+
}
|
|
3467
|
+
|
|
3468
|
+
if (!hasLink) return;
|
|
3469
|
+
|
|
3470
|
+
if (callbackResult === false) return;
|
|
3471
|
+
|
|
3402
3472
|
if (isTouch) {
|
|
3403
3473
|
// Touch: only open if already active
|
|
3404
3474
|
if (countryElement.classList.contains('svgMap-active')) {
|
|
3405
|
-
if (
|
|
3475
|
+
if (linkTarget) window.open(link, linkTarget);
|
|
3406
3476
|
else window.location.href = link;
|
|
3407
3477
|
} else {
|
|
3408
|
-
// first tap shows tooltip
|
|
3478
|
+
// first tap shows tooltip (or opens link immediately if tooltips are off)
|
|
3409
3479
|
if (activeCountry) activeCountry.classList.remove('svgMap-active');
|
|
3410
3480
|
activeCountry = countryElement;
|
|
3411
3481
|
countryElement.classList.add('svgMap-active');
|
|
3412
|
-
|
|
3413
|
-
|
|
3482
|
+
if (this.options.showTooltips) {
|
|
3483
|
+
this.setTooltipContent(this.getTooltipContent(countryID));
|
|
3484
|
+
this.showTooltip(e);
|
|
3485
|
+
} else {
|
|
3486
|
+
if (linkTarget) window.open(link, linkTarget);
|
|
3487
|
+
else window.location.href = link;
|
|
3488
|
+
}
|
|
3414
3489
|
}
|
|
3415
3490
|
} else {
|
|
3416
3491
|
// Desktop: open immediately
|
|
3417
|
-
if (
|
|
3492
|
+
if (linkTarget) window.open(link, linkTarget);
|
|
3418
3493
|
else window.location.href = link;
|
|
3419
3494
|
}
|
|
3420
3495
|
});
|
|
3421
3496
|
|
|
3497
|
+
this._clickTooltipOutsideHandler = function (ev) {
|
|
3498
|
+
if (!this.tooltip || !this.tooltip.classList.contains('svgMap-active')) {
|
|
3499
|
+
return;
|
|
3500
|
+
}
|
|
3501
|
+
var node = ev.target;
|
|
3502
|
+
if (
|
|
3503
|
+
node &&
|
|
3504
|
+
node.closest &&
|
|
3505
|
+
(node.closest('.svgMap-country') || node.closest('.svgMap-tooltip'))
|
|
3506
|
+
) {
|
|
3507
|
+
return;
|
|
3508
|
+
}
|
|
3509
|
+
this.hideTooltip();
|
|
3510
|
+
if (this.mapImage) {
|
|
3511
|
+
this.mapImage
|
|
3512
|
+
.querySelectorAll('.svgMap-country.svgMap-active')
|
|
3513
|
+
.forEach(function (el) {
|
|
3514
|
+
el.classList.remove('svgMap-active');
|
|
3515
|
+
});
|
|
3516
|
+
}
|
|
3517
|
+
}.bind(this);
|
|
3518
|
+
|
|
3422
3519
|
// Expose instance
|
|
3423
3520
|
var me = this;
|
|
3424
3521
|
|
|
@@ -3484,13 +3581,82 @@ class svgMap {
|
|
|
3484
3581
|
}
|
|
3485
3582
|
}
|
|
3486
3583
|
|
|
3584
|
+
// Create the persistent tooltips
|
|
3585
|
+
|
|
3586
|
+
createPersistentTooltips(countryElements) {
|
|
3587
|
+
if (this.persistentTooltipGroup) {
|
|
3588
|
+
this.persistentTooltipGroup.remove();
|
|
3589
|
+
}
|
|
3590
|
+
|
|
3591
|
+
this.persistentTooltipGroup = document.createElementNS(
|
|
3592
|
+
'http://www.w3.org/2000/svg',
|
|
3593
|
+
'g'
|
|
3594
|
+
);
|
|
3595
|
+
this.persistentTooltipGroup.classList.add('svgMap-persistent-tooltips');
|
|
3596
|
+
this.mapImage.appendChild(this.persistentTooltipGroup);
|
|
3597
|
+
|
|
3598
|
+
countryElements.forEach(
|
|
3599
|
+
function (countryElement) {
|
|
3600
|
+
var countryID = countryElement.dataset.id;
|
|
3601
|
+
if (!this.shouldShowTooltipOnLoad(countryID)) {
|
|
3602
|
+
return;
|
|
3603
|
+
}
|
|
3604
|
+
|
|
3605
|
+
var boundingBox = countryElement.getBBox();
|
|
3606
|
+
var tooltipPosition = {
|
|
3607
|
+
x: boundingBox.x + boundingBox.width / 2,
|
|
3608
|
+
y: boundingBox.y + boundingBox.height / 2
|
|
3609
|
+
};
|
|
3610
|
+
|
|
3611
|
+
var tooltipObject = document.createElementNS(
|
|
3612
|
+
'http://www.w3.org/2000/svg',
|
|
3613
|
+
'foreignObject'
|
|
3614
|
+
);
|
|
3615
|
+
tooltipObject.setAttribute('x', tooltipPosition.x);
|
|
3616
|
+
tooltipObject.setAttribute('y', tooltipPosition.y);
|
|
3617
|
+
tooltipObject.setAttribute('width', 1);
|
|
3618
|
+
tooltipObject.setAttribute('height', 1);
|
|
3619
|
+
tooltipObject.classList.add('svgMap-persistent-tooltip-wrapper');
|
|
3620
|
+
|
|
3621
|
+
var tooltipElement = this.createElement(
|
|
3622
|
+
'div',
|
|
3623
|
+
'svgMap-persistent-tooltip',
|
|
3624
|
+
tooltipObject
|
|
3625
|
+
);
|
|
3626
|
+
tooltipElement.append(
|
|
3627
|
+
this.getTooltipContent(countryID, tooltipElement)
|
|
3628
|
+
);
|
|
3629
|
+
this.createElement('div', 'svgMap-tooltip-pointer', tooltipElement);
|
|
3630
|
+
|
|
3631
|
+
this.persistentTooltipGroup.appendChild(tooltipObject);
|
|
3632
|
+
}.bind(this)
|
|
3633
|
+
);
|
|
3634
|
+
}
|
|
3635
|
+
|
|
3636
|
+
// Check if a persistent tooltip should be shown on load
|
|
3637
|
+
|
|
3638
|
+
shouldShowTooltipOnLoad(countryID) {
|
|
3639
|
+
var persistent = this.options.persistentTooltips;
|
|
3640
|
+
var countryValues = this.options.data.values[countryID];
|
|
3641
|
+
|
|
3642
|
+
if (Array.isArray(persistent)) {
|
|
3643
|
+
return persistent.indexOf(countryID) !== -1;
|
|
3644
|
+
}
|
|
3645
|
+
|
|
3646
|
+
if (typeof persistent === 'function') {
|
|
3647
|
+
return persistent(countryID, countryValues);
|
|
3648
|
+
}
|
|
3649
|
+
|
|
3650
|
+
return false;
|
|
3651
|
+
}
|
|
3652
|
+
|
|
3487
3653
|
// Create the tooltip content
|
|
3488
3654
|
|
|
3489
|
-
getTooltipContent(countryID) {
|
|
3655
|
+
getTooltipContent(countryID, tooltipDiv = this.tooltip) {
|
|
3490
3656
|
// Custom tooltip
|
|
3491
3657
|
if (this.options.onGetTooltip) {
|
|
3492
3658
|
var customDiv = this.options.onGetTooltip(
|
|
3493
|
-
|
|
3659
|
+
tooltipDiv,
|
|
3494
3660
|
countryID,
|
|
3495
3661
|
this.options.data.values[countryID]
|
|
3496
3662
|
);
|
|
@@ -4472,19 +4638,50 @@ class svgMap {
|
|
|
4472
4638
|
// Show the tooltip
|
|
4473
4639
|
|
|
4474
4640
|
showTooltip(e) {
|
|
4641
|
+
if (!this.tooltip) {
|
|
4642
|
+
return;
|
|
4643
|
+
}
|
|
4475
4644
|
this.tooltip.classList.add('svgMap-active');
|
|
4645
|
+
|
|
4646
|
+
if (
|
|
4647
|
+
this.options.showTooltips &&
|
|
4648
|
+
this.options.tooltipTrigger === 'click' &&
|
|
4649
|
+
e.pointerType === 'mouse'
|
|
4650
|
+
) {
|
|
4651
|
+
// don't register event listener in the same frame
|
|
4652
|
+
// to prevent it from being triggered immediately
|
|
4653
|
+
requestAnimationFrame(() => {
|
|
4654
|
+
document.addEventListener(
|
|
4655
|
+
'pointerdown',
|
|
4656
|
+
this._clickTooltipOutsideHandler,
|
|
4657
|
+
{ once: true, passive: true }
|
|
4658
|
+
);
|
|
4659
|
+
});
|
|
4660
|
+
}
|
|
4661
|
+
|
|
4476
4662
|
this.moveTooltip(e);
|
|
4477
4663
|
}
|
|
4478
4664
|
|
|
4479
4665
|
// Hide the tooltip
|
|
4480
4666
|
|
|
4481
4667
|
hideTooltip() {
|
|
4668
|
+
if (!this.tooltip) {
|
|
4669
|
+
return;
|
|
4670
|
+
}
|
|
4671
|
+
|
|
4482
4672
|
this.tooltip.classList.remove('svgMap-active');
|
|
4673
|
+
document.removeEventListener(
|
|
4674
|
+
'pointerdown',
|
|
4675
|
+
this._clickTooltipOutsideHandler
|
|
4676
|
+
);
|
|
4483
4677
|
}
|
|
4484
4678
|
|
|
4485
4679
|
// Move the tooltip
|
|
4486
4680
|
|
|
4487
4681
|
moveTooltip(e) {
|
|
4682
|
+
if (!this.tooltip) {
|
|
4683
|
+
return;
|
|
4684
|
+
}
|
|
4488
4685
|
var x = e.pageX || (e.touches && e.touches[0] ? e.touches[0].pageX : null);
|
|
4489
4686
|
var y = e.pageY || (e.touches && e.touches[0] ? e.touches[0].pageY : null);
|
|
4490
4687
|
|