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.js CHANGED
@@ -3225,6 +3225,102 @@ class svgMap {
3225
3225
 
3226
3226
  // Add map elements
3227
3227
  var countryElements = [];
3228
+
3229
+ const clearActive = function clearActive() {
3230
+ this.mapImage
3231
+ .querySelectorAll('.svgMap-active')
3232
+ .forEach((el) => el.classList.remove('svgMap-active'));
3233
+ }.bind(this);
3234
+
3235
+ const isClickTooltip =
3236
+ this.options.showTooltips && this.options.tooltipTrigger === 'click';
3237
+
3238
+ const getCountryFromEvent = function (e) {
3239
+ return e.target && e.target.closest
3240
+ ? e.target.closest('.svgMap-country')
3241
+ : null;
3242
+ };
3243
+
3244
+ const raiseCountry = function (countryElement, setActive) {
3245
+ if (setActive) {
3246
+ clearActive();
3247
+ }
3248
+ countryElement.parentNode.insertBefore(
3249
+ countryElement,
3250
+ this.persistentTooltipGroup || null
3251
+ );
3252
+ if (setActive) {
3253
+ countryElement.classList.add('svgMap-active');
3254
+ }
3255
+ }.bind(this);
3256
+
3257
+ const showCountryTooltip = function (countryElement, e, setActive) {
3258
+ raiseCountry(countryElement, setActive);
3259
+ this.setTooltipContent(this.getTooltipContent(countryElement.dataset.id));
3260
+ this.showTooltip(e);
3261
+ }.bind(this);
3262
+
3263
+ // Touch only: preview tooltip on finger down without marking country active
3264
+ // (active is set on pointerup so link countries keep two-tap navigation)
3265
+ this.mapImage.addEventListener(
3266
+ 'pointerdown',
3267
+ (e) => {
3268
+ if (!this.options.showTooltips || e.pointerType !== 'touch') {
3269
+ return;
3270
+ }
3271
+
3272
+ const countryElement = getCountryFromEvent(e);
3273
+ if (!countryElement) {
3274
+ this.hideTooltip();
3275
+ return;
3276
+ }
3277
+
3278
+ showCountryTooltip(countryElement, e, false);
3279
+ this.moveTooltip(e);
3280
+ },
3281
+ { passive: true }
3282
+ );
3283
+
3284
+ this.mapImage.addEventListener(
3285
+ 'pointercancel',
3286
+ (e) => {
3287
+ if (e.pointerType === 'touch') {
3288
+ this.hideTooltip();
3289
+ }
3290
+ },
3291
+ { passive: true }
3292
+ );
3293
+
3294
+ // Hover (mouse/pen) and touch drag: raise country + optional floating tooltip
3295
+ this.mapImage.addEventListener(
3296
+ 'pointermove',
3297
+ (e) => {
3298
+ const countryElement = getCountryFromEvent(e);
3299
+ if (!countryElement) {
3300
+ clearActive();
3301
+ if (this.options.showTooltips) {
3302
+ this.hideTooltip();
3303
+ }
3304
+ return;
3305
+ }
3306
+
3307
+ const mouseClickMode = e.pointerType === 'mouse' && isClickTooltip;
3308
+
3309
+ // Always raise hovered country (SVG paint order + .svgMap-active stroke)
3310
+ if (!this.options.showTooltips || mouseClickMode) {
3311
+ raiseCountry(countryElement, true);
3312
+ return;
3313
+ }
3314
+
3315
+ showCountryTooltip(countryElement, e, true);
3316
+
3317
+ if (e.pointerType === 'touch') {
3318
+ this.moveTooltip(e);
3319
+ }
3320
+ },
3321
+ { passive: true }
3322
+ );
3323
+
3228
3324
  Object.keys(mapPaths).forEach(
3229
3325
  function (countryID) {
3230
3326
  var countryData = this.mapPaths[countryID];
@@ -3242,166 +3338,22 @@ class svgMap {
3242
3338
  'id',
3243
3339
  this.id + '-map-country-' + countryID
3244
3340
  );
3245
- countryElement.setAttribute('data-id', countryID);
3341
+ countryElement.dataset.id = countryID;
3246
3342
  countryElement.classList.add('svgMap-country');
3247
3343
 
3248
3344
  this.mapImage.appendChild(countryElement);
3249
3345
  countryElements.push(countryElement);
3250
3346
 
3251
- // Add tooltip when touch is used
3252
- function handlePointerMove(e) {
3253
- if (e.pointerType === 'touch') return;
3254
-
3255
- const target = document.elementFromPoint(e.clientX, e.clientY);
3256
-
3257
- if (
3258
- !target ||
3259
- (!target.closest('.svgMap-country') &&
3260
- !target.closest('.svgMap-tooltip'))
3261
- ) {
3262
- this.hideTooltip();
3263
- document
3264
- .querySelectorAll('.svgMap-active')
3265
- .forEach((el) => el.classList.remove('svgMap-active'));
3266
- }
3267
- }
3268
-
3269
- const handlePointerMoveBound = handlePointerMove.bind(this);
3270
-
3271
- countryElement.addEventListener(
3272
- 'pointerenter',
3273
- function (e) {
3274
- if (
3275
- e.pointerType === 'mouse' &&
3276
- this.options.showTooltips &&
3277
- this.options.tooltipTrigger === 'click'
3278
- ) {
3279
- return;
3280
- }
3281
-
3282
- // Only add pointermove listener for non-touch pointers
3283
- if (e.pointerType !== 'touch') {
3284
- document.addEventListener('pointermove', handlePointerMoveBound, {
3285
- passive: true
3286
- });
3287
- }
3288
-
3289
- document
3290
- .querySelectorAll('.svgMap-active')
3291
- .forEach((el) => el.classList.remove('svgMap-active'));
3292
-
3293
- countryElement.parentNode.insertBefore(
3294
- countryElement,
3295
- this.persistentTooltipGroup || null
3296
- );
3297
- countryElement.classList.add('svgMap-active');
3298
-
3299
- const countryID = countryElement.getAttribute('data-id');
3300
- if (this.options.showTooltips) {
3301
- this.setTooltipContent(this.getTooltipContent(countryID));
3302
- this.showTooltip(e);
3303
-
3304
- // For touch, move tooltip to the touch position and keep it there
3305
- if (e.pointerType === 'touch') {
3306
- this.moveTooltip(e);
3307
- }
3308
- }
3309
- }.bind(this)
3310
- );
3311
-
3312
- // Handle touch move - update tooltip position while panning
3313
- countryElement.addEventListener(
3314
- 'touchmove',
3315
- function (e) {
3316
- this.moveTooltip(e);
3317
- }.bind(this),
3318
- { passive: true }
3319
- );
3320
-
3321
- // Handle touch end - remove active state and hide tooltip
3322
- countryElement.addEventListener(
3323
- 'touchend',
3324
- function (e) {
3325
- const touch = e.changedTouches[0];
3326
- const elementAtEnd = document.elementFromPoint(
3327
- touch.clientX,
3328
- touch.clientY
3329
- );
3330
-
3331
- // Only hide if touch ended outside the country or tooltip
3332
- if (
3333
- !elementAtEnd ||
3334
- (!elementAtEnd.closest('.svgMap-country') &&
3335
- !elementAtEnd.closest('.svgMap-tooltip'))
3336
- ) {
3337
- this.hideTooltip();
3338
- document
3339
- .querySelectorAll('.svgMap-active')
3340
- .forEach((el) => el.classList.remove('svgMap-active'));
3341
- }
3342
- }.bind(this),
3343
- { passive: true }
3344
- );
3345
-
3346
- // Remove pointermove listener when leaving non-touch pointer
3347
- countryElement.addEventListener(
3348
- 'pointerleave',
3349
- function (e) {
3350
- if (e.pointerType !== 'touch') {
3351
- document.removeEventListener(
3352
- 'pointermove',
3353
- handlePointerMoveBound
3354
- );
3355
- if (
3356
- !(
3357
- e.pointerType === 'mouse' &&
3358
- this.options.showTooltips &&
3359
- this.options.tooltipTrigger === 'click'
3360
- )
3361
- ) {
3362
- this.hideTooltip();
3363
- document
3364
- .querySelectorAll('.svgMap-active')
3365
- .forEach((el) => el.classList.remove('svgMap-active'));
3366
- }
3367
- }
3368
- }.bind(this)
3369
- );
3370
-
3371
- document.addEventListener(
3372
- 'pointerover',
3373
- function (e) {
3374
- if (e.pointerType !== 'touch') return;
3375
-
3376
- if (
3377
- e.target.closest('.svgMap-country') ||
3378
- e.target.closest('.svgMap-tooltip')
3379
- ) {
3380
- return;
3381
- }
3382
-
3383
- this.hideTooltip();
3384
- document
3385
- .querySelectorAll('.svgMap-active')
3386
- .forEach((el) => el.classList.remove('svgMap-active'));
3387
- }.bind(this),
3388
- { passive: true }
3389
- );
3390
-
3391
3347
  if (
3392
3348
  this.options.data.values &&
3393
3349
  this.options.data.values[countryID] &&
3394
3350
  this.options.data.values[countryID]['link']
3395
3351
  ) {
3396
- countryElement.setAttribute(
3397
- 'data-link',
3398
- this.options.data.values[countryID]['link']
3399
- );
3352
+ countryElement.dataset.link =
3353
+ this.options.data.values[countryID]['link'];
3400
3354
  if (this.options.data.values[countryID]['linkTarget']) {
3401
- countryElement.setAttribute(
3402
- 'data-link-target',
3403
- this.options.data.values[countryID]['linkTarget']
3404
- );
3355
+ countryElement.dataset.linkTarget =
3356
+ this.options.data.values[countryID]['linkTarget'];
3405
3357
  }
3406
3358
  }
3407
3359
  }.bind(this)
@@ -3447,21 +3399,18 @@ class svgMap {
3447
3399
  const countryElement = e.target.closest('.svgMap-country');
3448
3400
  if (!countryElement) return;
3449
3401
 
3450
- const countryID = countryElement.getAttribute('data-id');
3451
- const link = countryElement.getAttribute('data-link');
3452
- const linkTarget = countryElement.getAttribute('data-link-target');
3402
+ const countryID = countryElement.dataset.id;
3403
+ const link = countryElement.dataset.link;
3404
+ const linkTarget = countryElement.dataset.linkTarget;
3453
3405
  const hasCallback = typeof this.options.onCountryClick === 'function';
3454
3406
  const hasLink = !!link;
3455
3407
  const isTouch = e.pointerType === 'touch' || e.pointerType === 'pen';
3456
3408
 
3457
- const isClickTooltipMouse =
3458
- e.pointerType === 'mouse' &&
3459
- this.options.showTooltips &&
3460
- this.options.tooltipTrigger === 'click';
3409
+ const isClickTooltipMouse = e.pointerType === 'mouse' && isClickTooltip;
3461
3410
 
3462
3411
  if (!hasLink && !hasCallback && !isClickTooltipMouse) return;
3463
3412
 
3464
- if (isClickTooltipMouse && this.options.showTooltips) {
3413
+ if (isClickTooltipMouse) {
3465
3414
  const willNavigate =
3466
3415
  hasLink && countryElement.classList.contains('svgMap-active');
3467
3416
  const shouldFireCallback = hasCallback && (!hasLink || willNavigate);
@@ -3477,9 +3426,7 @@ class svgMap {
3477
3426
  if (linkTarget) window.open(link, linkTarget);
3478
3427
  else window.location.href = link;
3479
3428
  } else {
3480
- this.mapImage
3481
- .querySelectorAll('.svgMap-country.svgMap-active')
3482
- .forEach((el) => el.classList.remove('svgMap-active'));
3429
+ clearActive();
3483
3430
  countryElement.parentNode.insertBefore(
3484
3431
  countryElement,
3485
3432
  this.persistentTooltipGroup || null
@@ -3493,9 +3440,7 @@ class svgMap {
3493
3440
 
3494
3441
  if (callbackResultClick === false) return;
3495
3442
 
3496
- this.mapImage
3497
- .querySelectorAll('.svgMap-country.svgMap-active')
3498
- .forEach((el) => el.classList.remove('svgMap-active'));
3443
+ clearActive();
3499
3444
  countryElement.parentNode.insertBefore(
3500
3445
  countryElement,
3501
3446
  this.persistentTooltipGroup || null
@@ -3548,15 +3493,9 @@ class svgMap {
3548
3493
  });
3549
3494
 
3550
3495
  this._clickTooltipOutsideHandler = function (ev) {
3551
- if (ev.pointerType !== 'mouse') return;
3552
- if (
3553
- !this.options.showTooltips ||
3554
- this.options.tooltipTrigger !== 'click' ||
3555
- !this.tooltip
3556
- ) {
3496
+ if (!this.tooltip || !this.tooltip.classList.contains('svgMap-active')) {
3557
3497
  return;
3558
3498
  }
3559
- if (!this.tooltip.classList.contains('svgMap-active')) return;
3560
3499
  var node = ev.target;
3561
3500
  if (
3562
3501
  node &&
@@ -3574,11 +3513,6 @@ class svgMap {
3574
3513
  });
3575
3514
  }
3576
3515
  }.bind(this);
3577
- document.addEventListener(
3578
- 'pointerdown',
3579
- this._clickTooltipOutsideHandler,
3580
- true
3581
- );
3582
3516
 
3583
3517
  // Expose instance
3584
3518
  var me = this;
@@ -3661,7 +3595,7 @@ class svgMap {
3661
3595
 
3662
3596
  countryElements.forEach(
3663
3597
  function (countryElement) {
3664
- var countryID = countryElement.getAttribute('data-id');
3598
+ var countryID = countryElement.dataset.id;
3665
3599
  if (!this.shouldShowTooltipOnLoad(countryID)) {
3666
3600
  return;
3667
3601
  }
@@ -4706,6 +4640,23 @@ class svgMap {
4706
4640
  return;
4707
4641
  }
4708
4642
  this.tooltip.classList.add('svgMap-active');
4643
+
4644
+ if (
4645
+ this.options.showTooltips &&
4646
+ this.options.tooltipTrigger === 'click' &&
4647
+ e.pointerType === 'mouse'
4648
+ ) {
4649
+ // don't register event listener in the same frame
4650
+ // to prevent it from being triggered immediately
4651
+ requestAnimationFrame(() => {
4652
+ document.addEventListener(
4653
+ 'pointerdown',
4654
+ this._clickTooltipOutsideHandler,
4655
+ { once: true, passive: true }
4656
+ );
4657
+ });
4658
+ }
4659
+
4709
4660
  this.moveTooltip(e);
4710
4661
  }
4711
4662
 
@@ -4715,7 +4666,12 @@ class svgMap {
4715
4666
  if (!this.tooltip) {
4716
4667
  return;
4717
4668
  }
4669
+
4718
4670
  this.tooltip.classList.remove('svgMap-active');
4671
+ document.removeEventListener(
4672
+ 'pointerdown',
4673
+ this._clickTooltipOutsideHandler
4674
+ );
4719
4675
  }
4720
4676
 
4721
4677
  // Move the tooltip