svgmap 2.20.1 → 2.21.0

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 CHANGED
@@ -102,6 +102,17 @@ You can pass the following options into svgMap:
102
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
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
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'`. |
105
+ | `staticPins` | `false`, `array`, `function` | `false` | Static pins on the map at load time: an array of [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) country IDs (e.g. `['DE', 'FR']`), or a function (`function (countryID, countryValues) { … }`) to decide per country. Pins are placed at the geographic center of each country (largest landmass). Independent of `showTooltips`. |
106
+ | `pinColor` | `string` | `'#000000'` | Default fill color for circle pins. Accepts CSS vars, color names, rgb or hex values. Can be overridden per country via `data.values[id].pinColor`. |
107
+ | `pinStrokeColor` | `string` | `'#ffffff'` | Default stroke color for circle pins. Can be overridden per country via `data.values[id].pinStrokeColor`. |
108
+ | `pinStrokeWidth` | `number` | `1` | Default stroke width for circle pins, in screen pixels (non-scaling stroke). Can be overridden per country via `data.values[id].pinStrokeWidth`. Set to `0` for no stroke. |
109
+ | `pinSize` | `number` | `8` | Default radius for circle pins, in SVG units (viewBox is 2000 × 1001). Can be overridden per country via `data.values[id].pinSize`. |
110
+ | `pinImage` | `string` | | Image URL used as a pin instead of the default circle. Can be overridden per country via `data.values[id].pinImage`. |
111
+ | `pinImageWidth` | `number` | `20` | Width of the pin image in SVG units. Can be overridden per country via `data.values[id].pinImageWidth`. |
112
+ | `pinImageHeight` | `number` | `20` | Height of the pin image in SVG units. Can be overridden per country via `data.values[id].pinImageHeight`. |
113
+ | `pinOffsetX` | `number` | `0` | Horizontal offset from the pin position, in SVG units. Added after auto placement or `pinX`/`pinY`. Can be overridden per country via `data.values[id].pinOffsetX`. |
114
+ | `pinOffsetY` | `number` | `0` | Vertical offset from the pin position, in SVG units. Added after auto placement or `pinX`/`pinY`. Can be overridden per country via `data.values[id].pinOffsetY`. |
115
+ | `onGetPin` | `function` | | Custom pin element. Signature: `function (countryID, countryValues) { return svgElement; }`. Return an SVG element (e.g. `<g>`, `<path>`) to use instead of the default circle or image pin. The library positions it at the pin coordinates via `transform`. Return `null` to fall back to the default pin. |
105
116
  | `onGetTooltip` | `function` | | Called when a tooltip is created to custimize the tooltip content (`function (tooltipDiv, countryID, countryValues) { return 'Custom HTML'; }`) |
106
117
  | `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. |
107
118
  | `countries` | `object` | | Additional options specific to countries: |
@@ -117,6 +128,17 @@ You can pass the following options into svgMap:
117
128
  | &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`↳ color` | `string` | | Forces a color for this country |
118
129
  | &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`↳ link` | `string` | | An URL to redirect to when clicking the country |
119
130
  | &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`↳ linkTarget` | `string` | | The target of the link. By default the link will be opened in the same tab. Use `'_blank'` to open the link in a new tab |
131
+ | &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`↳ pinColor` | `string` | | Pin fill color for this country (circle pins only) |
132
+ | &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`↳ pinStrokeColor` | `string` | | Pin stroke color for this country (circle pins only) |
133
+ | &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`↳ pinStrokeWidth` | `number` | | Pin stroke width for this country, in screen pixels (circle pins only) |
134
+ | &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`↳ pinSize` | `number` | | Pin radius for this country (circle pins only) |
135
+ | &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`↳ pinX` | `number` | | Pin X position in SVG units; use with `pinY` to override auto placement |
136
+ | &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`↳ pinY` | `number` | | Pin Y position in SVG units; use with `pinX` to override auto placement |
137
+ | &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`↳ pinOffsetX` | `number` | | Horizontal offset from the pin position, in SVG units (after auto placement or `pinX`/`pinY`) |
138
+ | &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`↳ pinOffsetY` | `number` | | Vertical offset from the pin position, in SVG units (after auto placement or `pinX`/`pinY`) |
139
+ | &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`↳ pinImage` | `string` | | Image URL used as the pin for this country |
140
+ | &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`↳ pinImageWidth` | `number` | | Width of the pin image for this country |
141
+ | &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`↳ pinImageHeight` | `number` | | Height of the pin image for this country |
120
142
  | `countryNames` | `object` | | An object with the [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) country code as key and the country name as value |
121
143
  ---
122
144
 
package/dist/index.cjs CHANGED
@@ -2375,7 +2375,34 @@ class svgMap {
2375
2375
  showContinentSelector: false,
2376
2376
 
2377
2377
  // Reset zoom on resize
2378
- resetZoomOnResize: false
2378
+ resetZoomOnResize: false,
2379
+
2380
+ // Static pins: false | string[] | function(countryID, countryValues) => boolean
2381
+ staticPins: false,
2382
+
2383
+ // Default pin fill color
2384
+ pinColor: '#000000',
2385
+
2386
+ // Default pin stroke color and width (circle pins; width is in screen pixels with non-scaling stroke)
2387
+ pinStrokeColor: '#ffffff',
2388
+ pinStrokeWidth: 1.5,
2389
+
2390
+ // Default pin radius in SVG units (viewBox is 2000 × 1001)
2391
+ pinSize: 8,
2392
+
2393
+ // Custom pin element: function(countryID, countryValues) => SVGElement | null
2394
+ onGetPin: null,
2395
+
2396
+ // Image URL to use as a pin instead of the default circle (can also be set per-country via values[id].pinImage)
2397
+ pinImage: null,
2398
+
2399
+ // Width and height of the pin image in SVG units (viewBox is 2000 × 1001)
2400
+ pinImageWidth: 20,
2401
+ pinImageHeight: 20,
2402
+
2403
+ // Offset from computed pin position, in SVG units (added after auto center or pinX/pinY)
2404
+ pinOffsetX: 0,
2405
+ pinOffsetY: 0
2379
2406
  };
2380
2407
 
2381
2408
  this.options = Object.assign({}, defaultOptions, options);
@@ -3249,7 +3276,7 @@ class svgMap {
3249
3276
  }
3250
3277
  countryElement.parentNode.insertBefore(
3251
3278
  countryElement,
3252
- this.persistentTooltipGroup || null
3279
+ this.persistentTooltipGroup || this.pinGroup || null
3253
3280
  );
3254
3281
  if (setActive) {
3255
3282
  countryElement.classList.add('svgMap-active');
@@ -3369,6 +3396,10 @@ class svgMap {
3369
3396
  this.createPersistentTooltips(countryElements);
3370
3397
  }
3371
3398
 
3399
+ if (this.options.staticPins) {
3400
+ this.createStaticPins(countryElements);
3401
+ }
3402
+
3372
3403
  let pointerStart = null;
3373
3404
  let activeCountry = null;
3374
3405
 
@@ -3431,7 +3462,7 @@ class svgMap {
3431
3462
  clearActive();
3432
3463
  countryElement.parentNode.insertBefore(
3433
3464
  countryElement,
3434
- this.persistentTooltipGroup || null
3465
+ this.persistentTooltipGroup || this.pinGroup || null
3435
3466
  );
3436
3467
  countryElement.classList.add('svgMap-active');
3437
3468
  this.setTooltipContent(this.getTooltipContent(countryID));
@@ -3445,7 +3476,7 @@ class svgMap {
3445
3476
  clearActive();
3446
3477
  countryElement.parentNode.insertBefore(
3447
3478
  countryElement,
3448
- this.persistentTooltipGroup || null
3479
+ this.persistentTooltipGroup || this.pinGroup || null
3449
3480
  );
3450
3481
  countryElement.classList.add('svgMap-active');
3451
3482
  this.setTooltipContent(this.getTooltipContent(countryID));
@@ -3633,6 +3664,157 @@ class svgMap {
3633
3664
  );
3634
3665
  }
3635
3666
 
3667
+ // Create static pins on the map
3668
+
3669
+ createStaticPins(countryElements) {
3670
+ if (this.pinGroup) {
3671
+ this.pinGroup.remove();
3672
+ }
3673
+
3674
+ this.pinGroup = document.createElementNS('http://www.w3.org/2000/svg', 'g');
3675
+ this.pinGroup.classList.add('svgMap-pin-group');
3676
+ this.mapImage.appendChild(this.pinGroup);
3677
+
3678
+ countryElements.forEach(
3679
+ function (countryElement) {
3680
+ var countryID = countryElement.getAttribute('data-id');
3681
+ if (!this.shouldShowPin(countryID)) {
3682
+ return;
3683
+ }
3684
+
3685
+ var countryValues = this.options.data.values[countryID];
3686
+ var cx, cy;
3687
+
3688
+ if (
3689
+ countryValues &&
3690
+ countryValues.pinX != null &&
3691
+ countryValues.pinY != null
3692
+ ) {
3693
+ cx = countryValues.pinX;
3694
+ cy = countryValues.pinY;
3695
+ } else {
3696
+ // Split the path at absolute M commands and use the largest sub-path
3697
+ // to avoid overseas territories (islands, colonies) skewing the center.
3698
+ var d = countryElement.getAttribute('d');
3699
+ var subPaths = d.split(/(?=M)/).filter((s) => s.trim().length > 0);
3700
+ var largestBB = null;
3701
+ var largestArea = -1;
3702
+
3703
+ subPaths.forEach(
3704
+ function (subPath) {
3705
+ var tmp = document.createElementNS(
3706
+ 'http://www.w3.org/2000/svg',
3707
+ 'path'
3708
+ );
3709
+ tmp.setAttribute('d', subPath);
3710
+ this.mapImage.appendChild(tmp);
3711
+ var bb = tmp.getBBox();
3712
+ var area = bb.width * bb.height;
3713
+ if (area > largestArea) {
3714
+ largestArea = area;
3715
+ largestBB = bb;
3716
+ }
3717
+ this.mapImage.removeChild(tmp);
3718
+ }.bind(this)
3719
+ );
3720
+
3721
+ cx = largestBB.x + largestBB.width / 2;
3722
+ cy = largestBB.y + largestBB.height / 2;
3723
+ }
3724
+
3725
+ var offsetX =
3726
+ countryValues && countryValues.pinOffsetX != null
3727
+ ? countryValues.pinOffsetX
3728
+ : this.options.pinOffsetX;
3729
+ var offsetY =
3730
+ countryValues && countryValues.pinOffsetY != null
3731
+ ? countryValues.pinOffsetY
3732
+ : this.options.pinOffsetY;
3733
+ cx += offsetX;
3734
+ cy += offsetY;
3735
+
3736
+ var color =
3737
+ (countryValues && countryValues.pinColor) || this.options.pinColor;
3738
+ var size =
3739
+ (countryValues && countryValues.pinSize) || this.options.pinSize;
3740
+ var strokeColor =
3741
+ (countryValues && countryValues.pinStrokeColor) ||
3742
+ this.options.pinStrokeColor;
3743
+ var strokeWidth =
3744
+ (countryValues && countryValues.pinStrokeWidth) ||
3745
+ this.options.pinStrokeWidth;
3746
+
3747
+ if (typeof this.options.onGetPin === 'function') {
3748
+ var custom = this.options.onGetPin(countryID, countryValues);
3749
+ if (custom) {
3750
+ custom.setAttribute(
3751
+ 'transform',
3752
+ 'translate(' + cx + ',' + cy + ')'
3753
+ );
3754
+ this.pinGroup.appendChild(custom);
3755
+ return;
3756
+ }
3757
+ }
3758
+
3759
+ var pinImage =
3760
+ (countryValues && countryValues.pinImage) || this.options.pinImage;
3761
+
3762
+ if (pinImage) {
3763
+ var pinW =
3764
+ (countryValues && countryValues.pinImageWidth) ||
3765
+ this.options.pinImageWidth;
3766
+ var pinH =
3767
+ (countryValues && countryValues.pinImageHeight) ||
3768
+ this.options.pinImageHeight;
3769
+ var img = document.createElementNS(
3770
+ 'http://www.w3.org/2000/svg',
3771
+ 'image'
3772
+ );
3773
+ img.setAttribute('href', pinImage);
3774
+ img.setAttribute('x', cx - pinW / 2);
3775
+ img.setAttribute('y', cy - pinH / 2);
3776
+ img.setAttribute('width', pinW);
3777
+ img.setAttribute('height', pinH);
3778
+ img.setAttribute('data-id', countryID);
3779
+ img.classList.add('svgMap-pin');
3780
+ this.pinGroup.appendChild(img);
3781
+ return;
3782
+ }
3783
+
3784
+ var circle = document.createElementNS(
3785
+ 'http://www.w3.org/2000/svg',
3786
+ 'circle'
3787
+ );
3788
+ circle.setAttribute('cx', cx);
3789
+ circle.setAttribute('cy', cy);
3790
+ circle.setAttribute('r', size);
3791
+ circle.setAttribute('fill', color);
3792
+ if (strokeWidth > 0) {
3793
+ circle.setAttribute('stroke', strokeColor);
3794
+ circle.setAttribute('stroke-width', strokeWidth);
3795
+ circle.setAttribute('vector-effect', 'non-scaling-stroke');
3796
+ }
3797
+ circle.setAttribute('data-id', countryID);
3798
+ circle.classList.add('svgMap-pin');
3799
+ this.pinGroup.appendChild(circle);
3800
+ }.bind(this)
3801
+ );
3802
+ }
3803
+
3804
+ // Check if a static pin should be shown for a country
3805
+
3806
+ shouldShowPin(countryID) {
3807
+ var pins = this.options.staticPins;
3808
+ var countryValues = this.options.data.values[countryID];
3809
+ if (Array.isArray(pins)) {
3810
+ return pins.indexOf(countryID) !== -1;
3811
+ }
3812
+ if (typeof pins === 'function') {
3813
+ return pins(countryID, countryValues);
3814
+ }
3815
+ return false;
3816
+ }
3817
+
3636
3818
  // Check if a persistent tooltip should be shown on load
3637
3819
 
3638
3820
  shouldShowTooltipOnLoad(countryID) {