svgmap 2.20.0 → 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/dist/index.cjs CHANGED
@@ -3227,6 +3227,102 @@ class svgMap {
3227
3227
 
3228
3228
  // Add map elements
3229
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
+
3230
3326
  Object.keys(mapPaths).forEach(
3231
3327
  function (countryID) {
3232
3328
  var countryData = this.mapPaths[countryID];
@@ -3244,166 +3340,22 @@ class svgMap {
3244
3340
  'id',
3245
3341
  this.id + '-map-country-' + countryID
3246
3342
  );
3247
- countryElement.setAttribute('data-id', countryID);
3343
+ countryElement.dataset.id = countryID;
3248
3344
  countryElement.classList.add('svgMap-country');
3249
3345
 
3250
3346
  this.mapImage.appendChild(countryElement);
3251
3347
  countryElements.push(countryElement);
3252
3348
 
3253
- // Add tooltip when touch is used
3254
- function handlePointerMove(e) {
3255
- if (e.pointerType === 'touch') return;
3256
-
3257
- const target = document.elementFromPoint(e.clientX, e.clientY);
3258
-
3259
- if (
3260
- !target ||
3261
- (!target.closest('.svgMap-country') &&
3262
- !target.closest('.svgMap-tooltip'))
3263
- ) {
3264
- this.hideTooltip();
3265
- document
3266
- .querySelectorAll('.svgMap-active')
3267
- .forEach((el) => el.classList.remove('svgMap-active'));
3268
- }
3269
- }
3270
-
3271
- const handlePointerMoveBound = handlePointerMove.bind(this);
3272
-
3273
- countryElement.addEventListener(
3274
- 'pointerenter',
3275
- function (e) {
3276
- if (
3277
- e.pointerType === 'mouse' &&
3278
- this.options.showTooltips &&
3279
- this.options.tooltipTrigger === 'click'
3280
- ) {
3281
- return;
3282
- }
3283
-
3284
- // Only add pointermove listener for non-touch pointers
3285
- if (e.pointerType !== 'touch') {
3286
- document.addEventListener('pointermove', handlePointerMoveBound, {
3287
- passive: true
3288
- });
3289
- }
3290
-
3291
- document
3292
- .querySelectorAll('.svgMap-active')
3293
- .forEach((el) => el.classList.remove('svgMap-active'));
3294
-
3295
- countryElement.parentNode.insertBefore(
3296
- countryElement,
3297
- this.persistentTooltipGroup || null
3298
- );
3299
- countryElement.classList.add('svgMap-active');
3300
-
3301
- const countryID = countryElement.getAttribute('data-id');
3302
- if (this.options.showTooltips) {
3303
- this.setTooltipContent(this.getTooltipContent(countryID));
3304
- this.showTooltip(e);
3305
-
3306
- // For touch, move tooltip to the touch position and keep it there
3307
- if (e.pointerType === 'touch') {
3308
- this.moveTooltip(e);
3309
- }
3310
- }
3311
- }.bind(this)
3312
- );
3313
-
3314
- // Handle touch move - update tooltip position while panning
3315
- countryElement.addEventListener(
3316
- 'touchmove',
3317
- function (e) {
3318
- this.moveTooltip(e);
3319
- }.bind(this),
3320
- { passive: true }
3321
- );
3322
-
3323
- // Handle touch end - remove active state and hide tooltip
3324
- countryElement.addEventListener(
3325
- 'touchend',
3326
- function (e) {
3327
- const touch = e.changedTouches[0];
3328
- const elementAtEnd = document.elementFromPoint(
3329
- touch.clientX,
3330
- touch.clientY
3331
- );
3332
-
3333
- // Only hide if touch ended outside the country or tooltip
3334
- if (
3335
- !elementAtEnd ||
3336
- (!elementAtEnd.closest('.svgMap-country') &&
3337
- !elementAtEnd.closest('.svgMap-tooltip'))
3338
- ) {
3339
- this.hideTooltip();
3340
- document
3341
- .querySelectorAll('.svgMap-active')
3342
- .forEach((el) => el.classList.remove('svgMap-active'));
3343
- }
3344
- }.bind(this),
3345
- { passive: true }
3346
- );
3347
-
3348
- // Remove pointermove listener when leaving non-touch pointer
3349
- countryElement.addEventListener(
3350
- 'pointerleave',
3351
- function (e) {
3352
- if (e.pointerType !== 'touch') {
3353
- document.removeEventListener(
3354
- 'pointermove',
3355
- handlePointerMoveBound
3356
- );
3357
- if (
3358
- !(
3359
- e.pointerType === 'mouse' &&
3360
- this.options.showTooltips &&
3361
- this.options.tooltipTrigger === 'click'
3362
- )
3363
- ) {
3364
- this.hideTooltip();
3365
- document
3366
- .querySelectorAll('.svgMap-active')
3367
- .forEach((el) => el.classList.remove('svgMap-active'));
3368
- }
3369
- }
3370
- }.bind(this)
3371
- );
3372
-
3373
- document.addEventListener(
3374
- 'pointerover',
3375
- function (e) {
3376
- if (e.pointerType !== 'touch') return;
3377
-
3378
- if (
3379
- e.target.closest('.svgMap-country') ||
3380
- e.target.closest('.svgMap-tooltip')
3381
- ) {
3382
- return;
3383
- }
3384
-
3385
- this.hideTooltip();
3386
- document
3387
- .querySelectorAll('.svgMap-active')
3388
- .forEach((el) => el.classList.remove('svgMap-active'));
3389
- }.bind(this),
3390
- { passive: true }
3391
- );
3392
-
3393
3349
  if (
3394
3350
  this.options.data.values &&
3395
3351
  this.options.data.values[countryID] &&
3396
3352
  this.options.data.values[countryID]['link']
3397
3353
  ) {
3398
- countryElement.setAttribute(
3399
- 'data-link',
3400
- this.options.data.values[countryID]['link']
3401
- );
3354
+ countryElement.dataset.link =
3355
+ this.options.data.values[countryID]['link'];
3402
3356
  if (this.options.data.values[countryID]['linkTarget']) {
3403
- countryElement.setAttribute(
3404
- 'data-link-target',
3405
- this.options.data.values[countryID]['linkTarget']
3406
- );
3357
+ countryElement.dataset.linkTarget =
3358
+ this.options.data.values[countryID]['linkTarget'];
3407
3359
  }
3408
3360
  }
3409
3361
  }.bind(this)
@@ -3449,21 +3401,18 @@ class svgMap {
3449
3401
  const countryElement = e.target.closest('.svgMap-country');
3450
3402
  if (!countryElement) return;
3451
3403
 
3452
- const countryID = countryElement.getAttribute('data-id');
3453
- const link = countryElement.getAttribute('data-link');
3454
- const linkTarget = countryElement.getAttribute('data-link-target');
3404
+ const countryID = countryElement.dataset.id;
3405
+ const link = countryElement.dataset.link;
3406
+ const linkTarget = countryElement.dataset.linkTarget;
3455
3407
  const hasCallback = typeof this.options.onCountryClick === 'function';
3456
3408
  const hasLink = !!link;
3457
3409
  const isTouch = e.pointerType === 'touch' || e.pointerType === 'pen';
3458
3410
 
3459
- const isClickTooltipMouse =
3460
- e.pointerType === 'mouse' &&
3461
- this.options.showTooltips &&
3462
- this.options.tooltipTrigger === 'click';
3411
+ const isClickTooltipMouse = e.pointerType === 'mouse' && isClickTooltip;
3463
3412
 
3464
3413
  if (!hasLink && !hasCallback && !isClickTooltipMouse) return;
3465
3414
 
3466
- if (isClickTooltipMouse && this.options.showTooltips) {
3415
+ if (isClickTooltipMouse) {
3467
3416
  const willNavigate =
3468
3417
  hasLink && countryElement.classList.contains('svgMap-active');
3469
3418
  const shouldFireCallback = hasCallback && (!hasLink || willNavigate);
@@ -3479,9 +3428,7 @@ class svgMap {
3479
3428
  if (linkTarget) window.open(link, linkTarget);
3480
3429
  else window.location.href = link;
3481
3430
  } else {
3482
- this.mapImage
3483
- .querySelectorAll('.svgMap-country.svgMap-active')
3484
- .forEach((el) => el.classList.remove('svgMap-active'));
3431
+ clearActive();
3485
3432
  countryElement.parentNode.insertBefore(
3486
3433
  countryElement,
3487
3434
  this.persistentTooltipGroup || null
@@ -3495,9 +3442,7 @@ class svgMap {
3495
3442
 
3496
3443
  if (callbackResultClick === false) return;
3497
3444
 
3498
- this.mapImage
3499
- .querySelectorAll('.svgMap-country.svgMap-active')
3500
- .forEach((el) => el.classList.remove('svgMap-active'));
3445
+ clearActive();
3501
3446
  countryElement.parentNode.insertBefore(
3502
3447
  countryElement,
3503
3448
  this.persistentTooltipGroup || null
@@ -3550,15 +3495,9 @@ class svgMap {
3550
3495
  });
3551
3496
 
3552
3497
  this._clickTooltipOutsideHandler = function (ev) {
3553
- if (ev.pointerType !== 'mouse') return;
3554
- if (
3555
- !this.options.showTooltips ||
3556
- this.options.tooltipTrigger !== 'click' ||
3557
- !this.tooltip
3558
- ) {
3498
+ if (!this.tooltip || !this.tooltip.classList.contains('svgMap-active')) {
3559
3499
  return;
3560
3500
  }
3561
- if (!this.tooltip.classList.contains('svgMap-active')) return;
3562
3501
  var node = ev.target;
3563
3502
  if (
3564
3503
  node &&
@@ -3576,11 +3515,6 @@ class svgMap {
3576
3515
  });
3577
3516
  }
3578
3517
  }.bind(this);
3579
- document.addEventListener(
3580
- 'pointerdown',
3581
- this._clickTooltipOutsideHandler,
3582
- true
3583
- );
3584
3518
 
3585
3519
  // Expose instance
3586
3520
  var me = this;
@@ -3663,7 +3597,7 @@ class svgMap {
3663
3597
 
3664
3598
  countryElements.forEach(
3665
3599
  function (countryElement) {
3666
- var countryID = countryElement.getAttribute('data-id');
3600
+ var countryID = countryElement.dataset.id;
3667
3601
  if (!this.shouldShowTooltipOnLoad(countryID)) {
3668
3602
  return;
3669
3603
  }
@@ -4708,6 +4642,23 @@ class svgMap {
4708
4642
  return;
4709
4643
  }
4710
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
+
4711
4662
  this.moveTooltip(e);
4712
4663
  }
4713
4664
 
@@ -4717,7 +4668,12 @@ class svgMap {
4717
4668
  if (!this.tooltip) {
4718
4669
  return;
4719
4670
  }
4671
+
4720
4672
  this.tooltip.classList.remove('svgMap-active');
4673
+ document.removeEventListener(
4674
+ 'pointerdown',
4675
+ this._clickTooltipOutsideHandler
4676
+ );
4721
4677
  }
4722
4678
 
4723
4679
  // Move the tooltip