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/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.20.1",
4
+ "version": "2.21.0",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "keywords": [
@@ -101,7 +101,34 @@ export default class svgMap {
101
101
  showContinentSelector: false,
102
102
 
103
103
  // Reset zoom on resize
104
- resetZoomOnResize: false
104
+ resetZoomOnResize: false,
105
+
106
+ // Static pins: false | string[] | function(countryID, countryValues) => boolean
107
+ staticPins: false,
108
+
109
+ // Default pin fill color
110
+ pinColor: '#000000',
111
+
112
+ // Default pin stroke color and width (circle pins; width is in screen pixels with non-scaling stroke)
113
+ pinStrokeColor: '#ffffff',
114
+ pinStrokeWidth: 1.5,
115
+
116
+ // Default pin radius in SVG units (viewBox is 2000 × 1001)
117
+ pinSize: 8,
118
+
119
+ // Custom pin element: function(countryID, countryValues) => SVGElement | null
120
+ onGetPin: null,
121
+
122
+ // Image URL to use as a pin instead of the default circle (can also be set per-country via values[id].pinImage)
123
+ pinImage: null,
124
+
125
+ // Width and height of the pin image in SVG units (viewBox is 2000 × 1001)
126
+ pinImageWidth: 20,
127
+ pinImageHeight: 20,
128
+
129
+ // Offset from computed pin position, in SVG units (added after auto center or pinX/pinY)
130
+ pinOffsetX: 0,
131
+ pinOffsetY: 0
105
132
  };
106
133
 
107
134
  this.options = Object.assign({}, defaultOptions, options);
@@ -975,7 +1002,7 @@ export default class svgMap {
975
1002
  }
976
1003
  countryElement.parentNode.insertBefore(
977
1004
  countryElement,
978
- this.persistentTooltipGroup || null
1005
+ this.persistentTooltipGroup || this.pinGroup || null
979
1006
  );
980
1007
  if (setActive) {
981
1008
  countryElement.classList.add('svgMap-active');
@@ -1095,6 +1122,10 @@ export default class svgMap {
1095
1122
  this.createPersistentTooltips(countryElements);
1096
1123
  }
1097
1124
 
1125
+ if (this.options.staticPins) {
1126
+ this.createStaticPins(countryElements);
1127
+ }
1128
+
1098
1129
  let pointerStart = null;
1099
1130
  let activeCountry = null;
1100
1131
 
@@ -1157,7 +1188,7 @@ export default class svgMap {
1157
1188
  clearActive();
1158
1189
  countryElement.parentNode.insertBefore(
1159
1190
  countryElement,
1160
- this.persistentTooltipGroup || null
1191
+ this.persistentTooltipGroup || this.pinGroup || null
1161
1192
  );
1162
1193
  countryElement.classList.add('svgMap-active');
1163
1194
  this.setTooltipContent(this.getTooltipContent(countryID));
@@ -1171,7 +1202,7 @@ export default class svgMap {
1171
1202
  clearActive();
1172
1203
  countryElement.parentNode.insertBefore(
1173
1204
  countryElement,
1174
- this.persistentTooltipGroup || null
1205
+ this.persistentTooltipGroup || this.pinGroup || null
1175
1206
  );
1176
1207
  countryElement.classList.add('svgMap-active');
1177
1208
  this.setTooltipContent(this.getTooltipContent(countryID));
@@ -1359,6 +1390,157 @@ export default class svgMap {
1359
1390
  );
1360
1391
  }
1361
1392
 
1393
+ // Create static pins on the map
1394
+
1395
+ createStaticPins(countryElements) {
1396
+ if (this.pinGroup) {
1397
+ this.pinGroup.remove();
1398
+ }
1399
+
1400
+ this.pinGroup = document.createElementNS('http://www.w3.org/2000/svg', 'g');
1401
+ this.pinGroup.classList.add('svgMap-pin-group');
1402
+ this.mapImage.appendChild(this.pinGroup);
1403
+
1404
+ countryElements.forEach(
1405
+ function (countryElement) {
1406
+ var countryID = countryElement.getAttribute('data-id');
1407
+ if (!this.shouldShowPin(countryID)) {
1408
+ return;
1409
+ }
1410
+
1411
+ var countryValues = this.options.data.values[countryID];
1412
+ var cx, cy;
1413
+
1414
+ if (
1415
+ countryValues &&
1416
+ countryValues.pinX != null &&
1417
+ countryValues.pinY != null
1418
+ ) {
1419
+ cx = countryValues.pinX;
1420
+ cy = countryValues.pinY;
1421
+ } else {
1422
+ // Split the path at absolute M commands and use the largest sub-path
1423
+ // to avoid overseas territories (islands, colonies) skewing the center.
1424
+ var d = countryElement.getAttribute('d');
1425
+ var subPaths = d.split(/(?=M)/).filter((s) => s.trim().length > 0);
1426
+ var largestBB = null;
1427
+ var largestArea = -1;
1428
+
1429
+ subPaths.forEach(
1430
+ function (subPath) {
1431
+ var tmp = document.createElementNS(
1432
+ 'http://www.w3.org/2000/svg',
1433
+ 'path'
1434
+ );
1435
+ tmp.setAttribute('d', subPath);
1436
+ this.mapImage.appendChild(tmp);
1437
+ var bb = tmp.getBBox();
1438
+ var area = bb.width * bb.height;
1439
+ if (area > largestArea) {
1440
+ largestArea = area;
1441
+ largestBB = bb;
1442
+ }
1443
+ this.mapImage.removeChild(tmp);
1444
+ }.bind(this)
1445
+ );
1446
+
1447
+ cx = largestBB.x + largestBB.width / 2;
1448
+ cy = largestBB.y + largestBB.height / 2;
1449
+ }
1450
+
1451
+ var offsetX =
1452
+ countryValues && countryValues.pinOffsetX != null
1453
+ ? countryValues.pinOffsetX
1454
+ : this.options.pinOffsetX;
1455
+ var offsetY =
1456
+ countryValues && countryValues.pinOffsetY != null
1457
+ ? countryValues.pinOffsetY
1458
+ : this.options.pinOffsetY;
1459
+ cx += offsetX;
1460
+ cy += offsetY;
1461
+
1462
+ var color =
1463
+ (countryValues && countryValues.pinColor) || this.options.pinColor;
1464
+ var size =
1465
+ (countryValues && countryValues.pinSize) || this.options.pinSize;
1466
+ var strokeColor =
1467
+ (countryValues && countryValues.pinStrokeColor) ||
1468
+ this.options.pinStrokeColor;
1469
+ var strokeWidth =
1470
+ (countryValues && countryValues.pinStrokeWidth) ||
1471
+ this.options.pinStrokeWidth;
1472
+
1473
+ if (typeof this.options.onGetPin === 'function') {
1474
+ var custom = this.options.onGetPin(countryID, countryValues);
1475
+ if (custom) {
1476
+ custom.setAttribute(
1477
+ 'transform',
1478
+ 'translate(' + cx + ',' + cy + ')'
1479
+ );
1480
+ this.pinGroup.appendChild(custom);
1481
+ return;
1482
+ }
1483
+ }
1484
+
1485
+ var pinImage =
1486
+ (countryValues && countryValues.pinImage) || this.options.pinImage;
1487
+
1488
+ if (pinImage) {
1489
+ var pinW =
1490
+ (countryValues && countryValues.pinImageWidth) ||
1491
+ this.options.pinImageWidth;
1492
+ var pinH =
1493
+ (countryValues && countryValues.pinImageHeight) ||
1494
+ this.options.pinImageHeight;
1495
+ var img = document.createElementNS(
1496
+ 'http://www.w3.org/2000/svg',
1497
+ 'image'
1498
+ );
1499
+ img.setAttribute('href', pinImage);
1500
+ img.setAttribute('x', cx - pinW / 2);
1501
+ img.setAttribute('y', cy - pinH / 2);
1502
+ img.setAttribute('width', pinW);
1503
+ img.setAttribute('height', pinH);
1504
+ img.setAttribute('data-id', countryID);
1505
+ img.classList.add('svgMap-pin');
1506
+ this.pinGroup.appendChild(img);
1507
+ return;
1508
+ }
1509
+
1510
+ var circle = document.createElementNS(
1511
+ 'http://www.w3.org/2000/svg',
1512
+ 'circle'
1513
+ );
1514
+ circle.setAttribute('cx', cx);
1515
+ circle.setAttribute('cy', cy);
1516
+ circle.setAttribute('r', size);
1517
+ circle.setAttribute('fill', color);
1518
+ if (strokeWidth > 0) {
1519
+ circle.setAttribute('stroke', strokeColor);
1520
+ circle.setAttribute('stroke-width', strokeWidth);
1521
+ circle.setAttribute('vector-effect', 'non-scaling-stroke');
1522
+ }
1523
+ circle.setAttribute('data-id', countryID);
1524
+ circle.classList.add('svgMap-pin');
1525
+ this.pinGroup.appendChild(circle);
1526
+ }.bind(this)
1527
+ );
1528
+ }
1529
+
1530
+ // Check if a static pin should be shown for a country
1531
+
1532
+ shouldShowPin(countryID) {
1533
+ var pins = this.options.staticPins;
1534
+ var countryValues = this.options.data.values[countryID];
1535
+ if (Array.isArray(pins)) {
1536
+ return pins.indexOf(countryID) !== -1;
1537
+ }
1538
+ if (typeof pins === 'function') {
1539
+ return pins(countryID, countryValues);
1540
+ }
1541
+ return false;
1542
+ }
1543
+
1362
1544
  // Check if a persistent tooltip should be shown on load
1363
1545
 
1364
1546
  shouldShowTooltipOnLoad(countryID) {
package/src/scss/map.scss CHANGED
@@ -259,3 +259,14 @@
259
259
  .svgMap-wrapper.svgMap-country-click-callback .svgMap-map-wrapper .svgMap-country {
260
260
  cursor: pointer;
261
261
  }
262
+
263
+ // Static pins
264
+
265
+ .svgMap-pin-group {
266
+ pointer-events: none;
267
+ }
268
+
269
+ .svgMap-pin {
270
+ vector-effect: non-scaling-stroke;
271
+ transition: r 250ms;
272
+ }