@flourish/ui-styles 6.0.1 → 7.0.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/ui-styles.js CHANGED
@@ -57,7 +57,7 @@
57
57
  return this.parent.select(this.selector + " " + selector);
58
58
  };
59
59
 
60
- var DEFAULTS$3 = Object.freeze({
60
+ var DEFAULTS$4 = Object.freeze({
61
61
  font_size: 1,
62
62
  font_weight: "bold",
63
63
  height: 2,
@@ -65,8 +65,8 @@
65
65
 
66
66
  function GeneralControlsStyle(state_, selector_, layout_) {
67
67
  this._state = state_;
68
- for (var key in DEFAULTS$3) {
69
- if (this._state[key] === undefined) this._state[key] = DEFAULTS$3[key];
68
+ for (var key in DEFAULTS$4) {
69
+ if (this._state[key] === undefined) this._state[key] = DEFAULTS$4[key];
70
70
  }
71
71
 
72
72
  this._layout = layout_ || {};
@@ -629,14 +629,39 @@
629
629
  each: proto.each
630
630
  };
631
631
 
632
+ // This is an exception because mocha doesn't run in a browser context, so some
633
+ // browser methods aren't available.
634
+ const is_browser =
635
+ typeof window !== "undefined" &&
636
+ typeof document !== "undefined" &&
637
+ typeof document.createElement === "function" &&
638
+ typeof document.body?.appendChild === "function" &&
639
+ typeof HTMLElement !== "undefined";
640
+
632
641
  const canvas = document.createElement("canvas");
633
- canvas.getContext("2d");
642
+ const ctx = canvas.getContext("2d");
643
+
644
+ const tnums_svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
645
+ const tnums_text_element = document.createElementNS(
646
+ "http://www.w3.org/2000/svg",
647
+ "text",
648
+ );
649
+ tnums_svg.appendChild(tnums_text_element);
650
+ if (is_browser) {
651
+ canvas.style.position = "fixed";
652
+ canvas.style.left = "1000vw";
653
+ tnums_svg.style.position = "fixed";
654
+ tnums_svg.style.left = "1000vw";
655
+ document.body.appendChild(canvas);
656
+ document.body.appendChild(tnums_svg);
657
+ }
658
+ let is_browser_supporting_tnums_in_canvas = null;
634
659
 
635
660
  function getTextDirection() {
636
661
  return window.getComputedStyle(document.body).direction || "ltr";
637
662
  }
638
663
 
639
- function hexToColor(hex_string, opacity) {
664
+ function hexToColor$1(hex_string, opacity) {
640
665
  if (typeof hex_string != "string") return false;
641
666
  var c = color(hex_string);
642
667
  c.opacity = opacity !== undefined ? opacity : 1;
@@ -644,12 +669,77 @@
644
669
  }
645
670
 
646
671
  function hexToRgba(hex_string, opacity) {
647
- var c = hexToColor(hex_string, opacity);
672
+ var c = hexToColor$1(hex_string, opacity);
648
673
 
649
674
  return c ? c.toString() : false;
650
675
  }
651
676
 
652
- var DEFAULTS$2 = Object.freeze({
677
+ function checkBrowserTnumSupport() {
678
+ const test_tnum_string = "0123456789.,£";
679
+ const no_tnum_canvas = document.createElement("canvas");
680
+ const tnum_canvas = document.createElement("canvas");
681
+ const no_tnum_ctx = no_tnum_canvas.getContext("2d");
682
+ const tnum_ctx = tnum_canvas.getContext("2d");
683
+
684
+ no_tnum_canvas.setAttribute(
685
+ "style",
686
+ "font-feature-settings: normal; font-variant-numeric: normal; position: fixed; left: 1000vw;",
687
+ );
688
+ tnum_canvas.setAttribute(
689
+ "style",
690
+ 'font-feature-settings: "tnum" 1; font-variant-numeric: tabular-nums; position: fixed; left: 1000vw;',
691
+ );
692
+
693
+ no_tnum_ctx.font = tnum_ctx.font = ctx.font;
694
+
695
+ document.body.appendChild(no_tnum_canvas);
696
+ document.body.appendChild(tnum_canvas);
697
+
698
+ const width_no_tnum = no_tnum_ctx.measureText(test_tnum_string).width;
699
+ const width_tnum = tnum_ctx.measureText(test_tnum_string).width;
700
+
701
+ is_browser_supporting_tnums_in_canvas = width_no_tnum !== width_tnum;
702
+
703
+ document.body.removeChild(no_tnum_canvas);
704
+ document.body.removeChild(tnum_canvas);
705
+
706
+ return is_browser_supporting_tnums_in_canvas;
707
+ }
708
+
709
+ ctx.measureTextWithTnums = function (text) {
710
+ if (!is_browser) return ctx.measureText(text);
711
+ const computed_style = window.getComputedStyle(this.canvas);
712
+ const is_tnums_style_set =
713
+ (computed_style.fontFeatureSettings || "").includes("tnum") ||
714
+ (computed_style.fontVariantNumeric || "").includes("tabular-nums");
715
+
716
+ // No tnums set for this text, so measure using canvas
717
+ if (!is_tnums_style_set) return this.measureText(text);
718
+
719
+ // Tnums are set, check if browser supports it
720
+ if (is_browser_supporting_tnums_in_canvas === null) checkBrowserTnumSupport();
721
+
722
+ // Browser supports tnums in canvas, measure text
723
+ if (is_browser_supporting_tnums_in_canvas) return this.measureText(text);
724
+
725
+ // Browser does not support tnums in canvas, measure text using svg instead
726
+ tnums_text_element.textContent = text;
727
+ tnums_text_element.setAttribute(
728
+ "style",
729
+ `font: ${ctx.font}; font-feature-settings: 'tnum' 1; font-variant-numeric: tabular-nums; position: fixed; left: 1000vw;`,
730
+ );
731
+
732
+ let svg_text_width = tnums_text_element.getBBox().width;
733
+
734
+ // measureText in order to return a full TextMetrics object with the svg measured width
735
+ const measured_text = this.measureText(text);
736
+ return {
737
+ ...measured_text,
738
+ width: svg_text_width,
739
+ };
740
+ };
741
+
742
+ var DEFAULTS$3 = Object.freeze({
653
743
  background: null,
654
744
  font_color: null,
655
745
 
@@ -667,8 +757,8 @@
667
757
 
668
758
  function ButtonStyle(state_, selector_, layout_) {
669
759
  this._state = state_;
670
- for (var key in DEFAULTS$2) {
671
- if (this._state[key] === undefined) this._state[key] = DEFAULTS$2[key];
760
+ for (var key in DEFAULTS$3) {
761
+ if (this._state[key] === undefined) this._state[key] = DEFAULTS$3[key];
672
762
  }
673
763
 
674
764
  this._layout = layout_ || {};
@@ -773,7 +863,7 @@
773
863
  this._stylesheet.innerHTML = this._styles.print();
774
864
  };
775
865
 
776
- var DEFAULTS$1 = Object.freeze({
866
+ var DEFAULTS$2 = Object.freeze({
777
867
  background: null,
778
868
  font_color: null,
779
869
 
@@ -786,8 +876,8 @@
786
876
 
787
877
  function DropdownStyle(state_, selector_, layout_) {
788
878
  this._state = state_;
789
- for (var key in DEFAULTS$1) {
790
- if (this._state[key] === undefined) this._state[key] = DEFAULTS$1[key];
879
+ for (var key in DEFAULTS$2) {
880
+ if (this._state[key] === undefined) this._state[key] = DEFAULTS$2[key];
791
881
  }
792
882
 
793
883
  this._layout = layout_ || {};
@@ -933,7 +1023,7 @@
933
1023
  return rem * parseFloat(getComputedStyle(document.documentElement).fontSize);
934
1024
  }
935
1025
 
936
- var DEFAULTS = Object.freeze({
1026
+ var DEFAULTS$1 = Object.freeze({
937
1027
  play_color: null,
938
1028
  handle_color: null,
939
1029
  font_color: null,
@@ -948,8 +1038,8 @@
948
1038
 
949
1039
  function SliderStyle(state_, selector_, layout_) {
950
1040
  this._state = state_;
951
- for (var key in DEFAULTS) {
952
- if (this._state[key] === undefined) this._state[key] = DEFAULTS[key];
1041
+ for (var key in DEFAULTS$1) {
1042
+ if (this._state[key] === undefined) this._state[key] = DEFAULTS$1[key];
953
1043
  }
954
1044
 
955
1045
  this._layout = layout_ || {};
@@ -1044,6 +1134,1340 @@
1044
1134
  this._stylesheet.innerHTML = this._styles.print();
1045
1135
  };
1046
1136
 
1137
+ var xhtml = "http://www.w3.org/1999/xhtml";
1138
+
1139
+ var namespaces = {
1140
+ svg: "http://www.w3.org/2000/svg",
1141
+ xhtml: xhtml,
1142
+ xlink: "http://www.w3.org/1999/xlink",
1143
+ xml: "http://www.w3.org/XML/1998/namespace",
1144
+ xmlns: "http://www.w3.org/2000/xmlns/"
1145
+ };
1146
+
1147
+ function namespace(name) {
1148
+ var prefix = name += "", i = prefix.indexOf(":");
1149
+ if (i >= 0 && (prefix = name.slice(0, i)) !== "xmlns") name = name.slice(i + 1);
1150
+ return namespaces.hasOwnProperty(prefix) ? {space: namespaces[prefix], local: name} : name;
1151
+ }
1152
+
1153
+ function creatorInherit(name) {
1154
+ return function() {
1155
+ var document = this.ownerDocument,
1156
+ uri = this.namespaceURI;
1157
+ return uri === xhtml && document.documentElement.namespaceURI === xhtml
1158
+ ? document.createElement(name)
1159
+ : document.createElementNS(uri, name);
1160
+ };
1161
+ }
1162
+
1163
+ function creatorFixed(fullname) {
1164
+ return function() {
1165
+ return this.ownerDocument.createElementNS(fullname.space, fullname.local);
1166
+ };
1167
+ }
1168
+
1169
+ function creator(name) {
1170
+ var fullname = namespace(name);
1171
+ return (fullname.local
1172
+ ? creatorFixed
1173
+ : creatorInherit)(fullname);
1174
+ }
1175
+
1176
+ function none() {}
1177
+
1178
+ function selector(selector) {
1179
+ return selector == null ? none : function() {
1180
+ return this.querySelector(selector);
1181
+ };
1182
+ }
1183
+
1184
+ function selection_select(select) {
1185
+ if (typeof select !== "function") select = selector(select);
1186
+
1187
+ for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
1188
+ for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) {
1189
+ if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) {
1190
+ if ("__data__" in node) subnode.__data__ = node.__data__;
1191
+ subgroup[i] = subnode;
1192
+ }
1193
+ }
1194
+ }
1195
+
1196
+ return new Selection(subgroups, this._parents);
1197
+ }
1198
+
1199
+ function empty() {
1200
+ return [];
1201
+ }
1202
+
1203
+ function selectorAll(selector) {
1204
+ return selector == null ? empty : function() {
1205
+ return this.querySelectorAll(selector);
1206
+ };
1207
+ }
1208
+
1209
+ function selection_selectAll(select) {
1210
+ if (typeof select !== "function") select = selectorAll(select);
1211
+
1212
+ for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) {
1213
+ for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
1214
+ if (node = group[i]) {
1215
+ subgroups.push(select.call(node, node.__data__, i, group));
1216
+ parents.push(node);
1217
+ }
1218
+ }
1219
+ }
1220
+
1221
+ return new Selection(subgroups, parents);
1222
+ }
1223
+
1224
+ function matcher(selector) {
1225
+ return function() {
1226
+ return this.matches(selector);
1227
+ };
1228
+ }
1229
+
1230
+ function selection_filter(match) {
1231
+ if (typeof match !== "function") match = matcher(match);
1232
+
1233
+ for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
1234
+ for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) {
1235
+ if ((node = group[i]) && match.call(node, node.__data__, i, group)) {
1236
+ subgroup.push(node);
1237
+ }
1238
+ }
1239
+ }
1240
+
1241
+ return new Selection(subgroups, this._parents);
1242
+ }
1243
+
1244
+ function sparse(update) {
1245
+ return new Array(update.length);
1246
+ }
1247
+
1248
+ function selection_enter() {
1249
+ return new Selection(this._enter || this._groups.map(sparse), this._parents);
1250
+ }
1251
+
1252
+ function EnterNode(parent, datum) {
1253
+ this.ownerDocument = parent.ownerDocument;
1254
+ this.namespaceURI = parent.namespaceURI;
1255
+ this._next = null;
1256
+ this._parent = parent;
1257
+ this.__data__ = datum;
1258
+ }
1259
+
1260
+ EnterNode.prototype = {
1261
+ constructor: EnterNode,
1262
+ appendChild: function(child) { return this._parent.insertBefore(child, this._next); },
1263
+ insertBefore: function(child, next) { return this._parent.insertBefore(child, next); },
1264
+ querySelector: function(selector) { return this._parent.querySelector(selector); },
1265
+ querySelectorAll: function(selector) { return this._parent.querySelectorAll(selector); }
1266
+ };
1267
+
1268
+ function constant(x) {
1269
+ return function() {
1270
+ return x;
1271
+ };
1272
+ }
1273
+
1274
+ var keyPrefix = "$"; // Protect against keys like “__proto__”.
1275
+
1276
+ function bindIndex(parent, group, enter, update, exit, data) {
1277
+ var i = 0,
1278
+ node,
1279
+ groupLength = group.length,
1280
+ dataLength = data.length;
1281
+
1282
+ // Put any non-null nodes that fit into update.
1283
+ // Put any null nodes into enter.
1284
+ // Put any remaining data into enter.
1285
+ for (; i < dataLength; ++i) {
1286
+ if (node = group[i]) {
1287
+ node.__data__ = data[i];
1288
+ update[i] = node;
1289
+ } else {
1290
+ enter[i] = new EnterNode(parent, data[i]);
1291
+ }
1292
+ }
1293
+
1294
+ // Put any non-null nodes that don’t fit into exit.
1295
+ for (; i < groupLength; ++i) {
1296
+ if (node = group[i]) {
1297
+ exit[i] = node;
1298
+ }
1299
+ }
1300
+ }
1301
+
1302
+ function bindKey(parent, group, enter, update, exit, data, key) {
1303
+ var i,
1304
+ node,
1305
+ nodeByKeyValue = {},
1306
+ groupLength = group.length,
1307
+ dataLength = data.length,
1308
+ keyValues = new Array(groupLength),
1309
+ keyValue;
1310
+
1311
+ // Compute the key for each node.
1312
+ // If multiple nodes have the same key, the duplicates are added to exit.
1313
+ for (i = 0; i < groupLength; ++i) {
1314
+ if (node = group[i]) {
1315
+ keyValues[i] = keyValue = keyPrefix + key.call(node, node.__data__, i, group);
1316
+ if (keyValue in nodeByKeyValue) {
1317
+ exit[i] = node;
1318
+ } else {
1319
+ nodeByKeyValue[keyValue] = node;
1320
+ }
1321
+ }
1322
+ }
1323
+
1324
+ // Compute the key for each datum.
1325
+ // If there a node associated with this key, join and add it to update.
1326
+ // If there is not (or the key is a duplicate), add it to enter.
1327
+ for (i = 0; i < dataLength; ++i) {
1328
+ keyValue = keyPrefix + key.call(parent, data[i], i, data);
1329
+ if (node = nodeByKeyValue[keyValue]) {
1330
+ update[i] = node;
1331
+ node.__data__ = data[i];
1332
+ nodeByKeyValue[keyValue] = null;
1333
+ } else {
1334
+ enter[i] = new EnterNode(parent, data[i]);
1335
+ }
1336
+ }
1337
+
1338
+ // Add any remaining nodes that were not bound to data to exit.
1339
+ for (i = 0; i < groupLength; ++i) {
1340
+ if ((node = group[i]) && (nodeByKeyValue[keyValues[i]] === node)) {
1341
+ exit[i] = node;
1342
+ }
1343
+ }
1344
+ }
1345
+
1346
+ function selection_data(value, key) {
1347
+ if (!value) {
1348
+ data = new Array(this.size()), j = -1;
1349
+ this.each(function(d) { data[++j] = d; });
1350
+ return data;
1351
+ }
1352
+
1353
+ var bind = key ? bindKey : bindIndex,
1354
+ parents = this._parents,
1355
+ groups = this._groups;
1356
+
1357
+ if (typeof value !== "function") value = constant(value);
1358
+
1359
+ for (var m = groups.length, update = new Array(m), enter = new Array(m), exit = new Array(m), j = 0; j < m; ++j) {
1360
+ var parent = parents[j],
1361
+ group = groups[j],
1362
+ groupLength = group.length,
1363
+ data = value.call(parent, parent && parent.__data__, j, parents),
1364
+ dataLength = data.length,
1365
+ enterGroup = enter[j] = new Array(dataLength),
1366
+ updateGroup = update[j] = new Array(dataLength),
1367
+ exitGroup = exit[j] = new Array(groupLength);
1368
+
1369
+ bind(parent, group, enterGroup, updateGroup, exitGroup, data, key);
1370
+
1371
+ // Now connect the enter nodes to their following update node, such that
1372
+ // appendChild can insert the materialized enter node before this node,
1373
+ // rather than at the end of the parent node.
1374
+ for (var i0 = 0, i1 = 0, previous, next; i0 < dataLength; ++i0) {
1375
+ if (previous = enterGroup[i0]) {
1376
+ if (i0 >= i1) i1 = i0 + 1;
1377
+ while (!(next = updateGroup[i1]) && ++i1 < dataLength);
1378
+ previous._next = next || null;
1379
+ }
1380
+ }
1381
+ }
1382
+
1383
+ update = new Selection(update, parents);
1384
+ update._enter = enter;
1385
+ update._exit = exit;
1386
+ return update;
1387
+ }
1388
+
1389
+ function selection_exit() {
1390
+ return new Selection(this._exit || this._groups.map(sparse), this._parents);
1391
+ }
1392
+
1393
+ function selection_join(onenter, onupdate, onexit) {
1394
+ var enter = this.enter(), update = this, exit = this.exit();
1395
+ enter = typeof onenter === "function" ? onenter(enter) : enter.append(onenter + "");
1396
+ if (onupdate != null) update = onupdate(update);
1397
+ if (onexit == null) exit.remove(); else onexit(exit);
1398
+ return enter && update ? enter.merge(update).order() : update;
1399
+ }
1400
+
1401
+ function selection_merge(selection) {
1402
+
1403
+ for (var groups0 = this._groups, groups1 = selection._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) {
1404
+ for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) {
1405
+ if (node = group0[i] || group1[i]) {
1406
+ merge[i] = node;
1407
+ }
1408
+ }
1409
+ }
1410
+
1411
+ for (; j < m0; ++j) {
1412
+ merges[j] = groups0[j];
1413
+ }
1414
+
1415
+ return new Selection(merges, this._parents);
1416
+ }
1417
+
1418
+ function selection_order() {
1419
+
1420
+ for (var groups = this._groups, j = -1, m = groups.length; ++j < m;) {
1421
+ for (var group = groups[j], i = group.length - 1, next = group[i], node; --i >= 0;) {
1422
+ if (node = group[i]) {
1423
+ if (next && node.compareDocumentPosition(next) ^ 4) next.parentNode.insertBefore(node, next);
1424
+ next = node;
1425
+ }
1426
+ }
1427
+ }
1428
+
1429
+ return this;
1430
+ }
1431
+
1432
+ function selection_sort(compare) {
1433
+ if (!compare) compare = ascending;
1434
+
1435
+ function compareNode(a, b) {
1436
+ return a && b ? compare(a.__data__, b.__data__) : !a - !b;
1437
+ }
1438
+
1439
+ for (var groups = this._groups, m = groups.length, sortgroups = new Array(m), j = 0; j < m; ++j) {
1440
+ for (var group = groups[j], n = group.length, sortgroup = sortgroups[j] = new Array(n), node, i = 0; i < n; ++i) {
1441
+ if (node = group[i]) {
1442
+ sortgroup[i] = node;
1443
+ }
1444
+ }
1445
+ sortgroup.sort(compareNode);
1446
+ }
1447
+
1448
+ return new Selection(sortgroups, this._parents).order();
1449
+ }
1450
+
1451
+ function ascending(a, b) {
1452
+ return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
1453
+ }
1454
+
1455
+ function selection_call() {
1456
+ var callback = arguments[0];
1457
+ arguments[0] = this;
1458
+ callback.apply(null, arguments);
1459
+ return this;
1460
+ }
1461
+
1462
+ function selection_nodes() {
1463
+ var nodes = new Array(this.size()), i = -1;
1464
+ this.each(function() { nodes[++i] = this; });
1465
+ return nodes;
1466
+ }
1467
+
1468
+ function selection_node() {
1469
+
1470
+ for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {
1471
+ for (var group = groups[j], i = 0, n = group.length; i < n; ++i) {
1472
+ var node = group[i];
1473
+ if (node) return node;
1474
+ }
1475
+ }
1476
+
1477
+ return null;
1478
+ }
1479
+
1480
+ function selection_size() {
1481
+ var size = 0;
1482
+ this.each(function() { ++size; });
1483
+ return size;
1484
+ }
1485
+
1486
+ function selection_empty() {
1487
+ return !this.node();
1488
+ }
1489
+
1490
+ function selection_each(callback) {
1491
+
1492
+ for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {
1493
+ for (var group = groups[j], i = 0, n = group.length, node; i < n; ++i) {
1494
+ if (node = group[i]) callback.call(node, node.__data__, i, group);
1495
+ }
1496
+ }
1497
+
1498
+ return this;
1499
+ }
1500
+
1501
+ function attrRemove(name) {
1502
+ return function() {
1503
+ this.removeAttribute(name);
1504
+ };
1505
+ }
1506
+
1507
+ function attrRemoveNS(fullname) {
1508
+ return function() {
1509
+ this.removeAttributeNS(fullname.space, fullname.local);
1510
+ };
1511
+ }
1512
+
1513
+ function attrConstant(name, value) {
1514
+ return function() {
1515
+ this.setAttribute(name, value);
1516
+ };
1517
+ }
1518
+
1519
+ function attrConstantNS(fullname, value) {
1520
+ return function() {
1521
+ this.setAttributeNS(fullname.space, fullname.local, value);
1522
+ };
1523
+ }
1524
+
1525
+ function attrFunction(name, value) {
1526
+ return function() {
1527
+ var v = value.apply(this, arguments);
1528
+ if (v == null) this.removeAttribute(name);
1529
+ else this.setAttribute(name, v);
1530
+ };
1531
+ }
1532
+
1533
+ function attrFunctionNS(fullname, value) {
1534
+ return function() {
1535
+ var v = value.apply(this, arguments);
1536
+ if (v == null) this.removeAttributeNS(fullname.space, fullname.local);
1537
+ else this.setAttributeNS(fullname.space, fullname.local, v);
1538
+ };
1539
+ }
1540
+
1541
+ function selection_attr(name, value) {
1542
+ var fullname = namespace(name);
1543
+
1544
+ if (arguments.length < 2) {
1545
+ var node = this.node();
1546
+ return fullname.local
1547
+ ? node.getAttributeNS(fullname.space, fullname.local)
1548
+ : node.getAttribute(fullname);
1549
+ }
1550
+
1551
+ return this.each((value == null
1552
+ ? (fullname.local ? attrRemoveNS : attrRemove) : (typeof value === "function"
1553
+ ? (fullname.local ? attrFunctionNS : attrFunction)
1554
+ : (fullname.local ? attrConstantNS : attrConstant)))(fullname, value));
1555
+ }
1556
+
1557
+ function defaultView(node) {
1558
+ return (node.ownerDocument && node.ownerDocument.defaultView) // node is a Node
1559
+ || (node.document && node) // node is a Window
1560
+ || node.defaultView; // node is a Document
1561
+ }
1562
+
1563
+ function styleRemove(name) {
1564
+ return function() {
1565
+ this.style.removeProperty(name);
1566
+ };
1567
+ }
1568
+
1569
+ function styleConstant(name, value, priority) {
1570
+ return function() {
1571
+ this.style.setProperty(name, value, priority);
1572
+ };
1573
+ }
1574
+
1575
+ function styleFunction(name, value, priority) {
1576
+ return function() {
1577
+ var v = value.apply(this, arguments);
1578
+ if (v == null) this.style.removeProperty(name);
1579
+ else this.style.setProperty(name, v, priority);
1580
+ };
1581
+ }
1582
+
1583
+ function selection_style(name, value, priority) {
1584
+ return arguments.length > 1
1585
+ ? this.each((value == null
1586
+ ? styleRemove : typeof value === "function"
1587
+ ? styleFunction
1588
+ : styleConstant)(name, value, priority == null ? "" : priority))
1589
+ : styleValue(this.node(), name);
1590
+ }
1591
+
1592
+ function styleValue(node, name) {
1593
+ return node.style.getPropertyValue(name)
1594
+ || defaultView(node).getComputedStyle(node, null).getPropertyValue(name);
1595
+ }
1596
+
1597
+ function propertyRemove(name) {
1598
+ return function() {
1599
+ delete this[name];
1600
+ };
1601
+ }
1602
+
1603
+ function propertyConstant(name, value) {
1604
+ return function() {
1605
+ this[name] = value;
1606
+ };
1607
+ }
1608
+
1609
+ function propertyFunction(name, value) {
1610
+ return function() {
1611
+ var v = value.apply(this, arguments);
1612
+ if (v == null) delete this[name];
1613
+ else this[name] = v;
1614
+ };
1615
+ }
1616
+
1617
+ function selection_property(name, value) {
1618
+ return arguments.length > 1
1619
+ ? this.each((value == null
1620
+ ? propertyRemove : typeof value === "function"
1621
+ ? propertyFunction
1622
+ : propertyConstant)(name, value))
1623
+ : this.node()[name];
1624
+ }
1625
+
1626
+ function classArray(string) {
1627
+ return string.trim().split(/^|\s+/);
1628
+ }
1629
+
1630
+ function classList(node) {
1631
+ return node.classList || new ClassList(node);
1632
+ }
1633
+
1634
+ function ClassList(node) {
1635
+ this._node = node;
1636
+ this._names = classArray(node.getAttribute("class") || "");
1637
+ }
1638
+
1639
+ ClassList.prototype = {
1640
+ add: function(name) {
1641
+ var i = this._names.indexOf(name);
1642
+ if (i < 0) {
1643
+ this._names.push(name);
1644
+ this._node.setAttribute("class", this._names.join(" "));
1645
+ }
1646
+ },
1647
+ remove: function(name) {
1648
+ var i = this._names.indexOf(name);
1649
+ if (i >= 0) {
1650
+ this._names.splice(i, 1);
1651
+ this._node.setAttribute("class", this._names.join(" "));
1652
+ }
1653
+ },
1654
+ contains: function(name) {
1655
+ return this._names.indexOf(name) >= 0;
1656
+ }
1657
+ };
1658
+
1659
+ function classedAdd(node, names) {
1660
+ var list = classList(node), i = -1, n = names.length;
1661
+ while (++i < n) list.add(names[i]);
1662
+ }
1663
+
1664
+ function classedRemove(node, names) {
1665
+ var list = classList(node), i = -1, n = names.length;
1666
+ while (++i < n) list.remove(names[i]);
1667
+ }
1668
+
1669
+ function classedTrue(names) {
1670
+ return function() {
1671
+ classedAdd(this, names);
1672
+ };
1673
+ }
1674
+
1675
+ function classedFalse(names) {
1676
+ return function() {
1677
+ classedRemove(this, names);
1678
+ };
1679
+ }
1680
+
1681
+ function classedFunction(names, value) {
1682
+ return function() {
1683
+ (value.apply(this, arguments) ? classedAdd : classedRemove)(this, names);
1684
+ };
1685
+ }
1686
+
1687
+ function selection_classed(name, value) {
1688
+ var names = classArray(name + "");
1689
+
1690
+ if (arguments.length < 2) {
1691
+ var list = classList(this.node()), i = -1, n = names.length;
1692
+ while (++i < n) if (!list.contains(names[i])) return false;
1693
+ return true;
1694
+ }
1695
+
1696
+ return this.each((typeof value === "function"
1697
+ ? classedFunction : value
1698
+ ? classedTrue
1699
+ : classedFalse)(names, value));
1700
+ }
1701
+
1702
+ function textRemove() {
1703
+ this.textContent = "";
1704
+ }
1705
+
1706
+ function textConstant(value) {
1707
+ return function() {
1708
+ this.textContent = value;
1709
+ };
1710
+ }
1711
+
1712
+ function textFunction(value) {
1713
+ return function() {
1714
+ var v = value.apply(this, arguments);
1715
+ this.textContent = v == null ? "" : v;
1716
+ };
1717
+ }
1718
+
1719
+ function selection_text(value) {
1720
+ return arguments.length
1721
+ ? this.each(value == null
1722
+ ? textRemove : (typeof value === "function"
1723
+ ? textFunction
1724
+ : textConstant)(value))
1725
+ : this.node().textContent;
1726
+ }
1727
+
1728
+ function htmlRemove() {
1729
+ this.innerHTML = "";
1730
+ }
1731
+
1732
+ function htmlConstant(value) {
1733
+ return function() {
1734
+ this.innerHTML = value;
1735
+ };
1736
+ }
1737
+
1738
+ function htmlFunction(value) {
1739
+ return function() {
1740
+ var v = value.apply(this, arguments);
1741
+ this.innerHTML = v == null ? "" : v;
1742
+ };
1743
+ }
1744
+
1745
+ function selection_html(value) {
1746
+ return arguments.length
1747
+ ? this.each(value == null
1748
+ ? htmlRemove : (typeof value === "function"
1749
+ ? htmlFunction
1750
+ : htmlConstant)(value))
1751
+ : this.node().innerHTML;
1752
+ }
1753
+
1754
+ function raise() {
1755
+ if (this.nextSibling) this.parentNode.appendChild(this);
1756
+ }
1757
+
1758
+ function selection_raise() {
1759
+ return this.each(raise);
1760
+ }
1761
+
1762
+ function lower() {
1763
+ if (this.previousSibling) this.parentNode.insertBefore(this, this.parentNode.firstChild);
1764
+ }
1765
+
1766
+ function selection_lower() {
1767
+ return this.each(lower);
1768
+ }
1769
+
1770
+ function selection_append(name) {
1771
+ var create = typeof name === "function" ? name : creator(name);
1772
+ return this.select(function() {
1773
+ return this.appendChild(create.apply(this, arguments));
1774
+ });
1775
+ }
1776
+
1777
+ function constantNull() {
1778
+ return null;
1779
+ }
1780
+
1781
+ function selection_insert(name, before) {
1782
+ var create = typeof name === "function" ? name : creator(name),
1783
+ select = before == null ? constantNull : typeof before === "function" ? before : selector(before);
1784
+ return this.select(function() {
1785
+ return this.insertBefore(create.apply(this, arguments), select.apply(this, arguments) || null);
1786
+ });
1787
+ }
1788
+
1789
+ function remove() {
1790
+ var parent = this.parentNode;
1791
+ if (parent) parent.removeChild(this);
1792
+ }
1793
+
1794
+ function selection_remove() {
1795
+ return this.each(remove);
1796
+ }
1797
+
1798
+ function selection_cloneShallow() {
1799
+ var clone = this.cloneNode(false), parent = this.parentNode;
1800
+ return parent ? parent.insertBefore(clone, this.nextSibling) : clone;
1801
+ }
1802
+
1803
+ function selection_cloneDeep() {
1804
+ var clone = this.cloneNode(true), parent = this.parentNode;
1805
+ return parent ? parent.insertBefore(clone, this.nextSibling) : clone;
1806
+ }
1807
+
1808
+ function selection_clone(deep) {
1809
+ return this.select(deep ? selection_cloneDeep : selection_cloneShallow);
1810
+ }
1811
+
1812
+ function selection_datum(value) {
1813
+ return arguments.length
1814
+ ? this.property("__data__", value)
1815
+ : this.node().__data__;
1816
+ }
1817
+
1818
+ var filterEvents = {};
1819
+
1820
+ if (typeof document !== "undefined") {
1821
+ var element = document.documentElement;
1822
+ if (!("onmouseenter" in element)) {
1823
+ filterEvents = {mouseenter: "mouseover", mouseleave: "mouseout"};
1824
+ }
1825
+ }
1826
+
1827
+ function filterContextListener(listener, index, group) {
1828
+ listener = contextListener(listener, index, group);
1829
+ return function(event) {
1830
+ var related = event.relatedTarget;
1831
+ if (!related || (related !== this && !(related.compareDocumentPosition(this) & 8))) {
1832
+ listener.call(this, event);
1833
+ }
1834
+ };
1835
+ }
1836
+
1837
+ function contextListener(listener, index, group) {
1838
+ return function(event1) {
1839
+ try {
1840
+ listener.call(this, this.__data__, index, group);
1841
+ } finally {
1842
+ }
1843
+ };
1844
+ }
1845
+
1846
+ function parseTypenames(typenames) {
1847
+ return typenames.trim().split(/^|\s+/).map(function(t) {
1848
+ var name = "", i = t.indexOf(".");
1849
+ if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);
1850
+ return {type: t, name: name};
1851
+ });
1852
+ }
1853
+
1854
+ function onRemove(typename) {
1855
+ return function() {
1856
+ var on = this.__on;
1857
+ if (!on) return;
1858
+ for (var j = 0, i = -1, m = on.length, o; j < m; ++j) {
1859
+ if (o = on[j], (!typename.type || o.type === typename.type) && o.name === typename.name) {
1860
+ this.removeEventListener(o.type, o.listener, o.capture);
1861
+ } else {
1862
+ on[++i] = o;
1863
+ }
1864
+ }
1865
+ if (++i) on.length = i;
1866
+ else delete this.__on;
1867
+ };
1868
+ }
1869
+
1870
+ function onAdd(typename, value, capture) {
1871
+ var wrap = filterEvents.hasOwnProperty(typename.type) ? filterContextListener : contextListener;
1872
+ return function(d, i, group) {
1873
+ var on = this.__on, o, listener = wrap(value, i, group);
1874
+ if (on) for (var j = 0, m = on.length; j < m; ++j) {
1875
+ if ((o = on[j]).type === typename.type && o.name === typename.name) {
1876
+ this.removeEventListener(o.type, o.listener, o.capture);
1877
+ this.addEventListener(o.type, o.listener = listener, o.capture = capture);
1878
+ o.value = value;
1879
+ return;
1880
+ }
1881
+ }
1882
+ this.addEventListener(typename.type, listener, capture);
1883
+ o = {type: typename.type, name: typename.name, value: value, listener: listener, capture: capture};
1884
+ if (!on) this.__on = [o];
1885
+ else on.push(o);
1886
+ };
1887
+ }
1888
+
1889
+ function selection_on(typename, value, capture) {
1890
+ var typenames = parseTypenames(typename + ""), i, n = typenames.length, t;
1891
+
1892
+ if (arguments.length < 2) {
1893
+ var on = this.node().__on;
1894
+ if (on) for (var j = 0, m = on.length, o; j < m; ++j) {
1895
+ for (i = 0, o = on[j]; i < n; ++i) {
1896
+ if ((t = typenames[i]).type === o.type && t.name === o.name) {
1897
+ return o.value;
1898
+ }
1899
+ }
1900
+ }
1901
+ return;
1902
+ }
1903
+
1904
+ on = value ? onAdd : onRemove;
1905
+ if (capture == null) capture = false;
1906
+ for (i = 0; i < n; ++i) this.each(on(typenames[i], value, capture));
1907
+ return this;
1908
+ }
1909
+
1910
+ function dispatchEvent(node, type, params) {
1911
+ var window = defaultView(node),
1912
+ event = window.CustomEvent;
1913
+
1914
+ if (typeof event === "function") {
1915
+ event = new event(type, params);
1916
+ } else {
1917
+ event = window.document.createEvent("Event");
1918
+ if (params) event.initEvent(type, params.bubbles, params.cancelable), event.detail = params.detail;
1919
+ else event.initEvent(type, false, false);
1920
+ }
1921
+
1922
+ node.dispatchEvent(event);
1923
+ }
1924
+
1925
+ function dispatchConstant(type, params) {
1926
+ return function() {
1927
+ return dispatchEvent(this, type, params);
1928
+ };
1929
+ }
1930
+
1931
+ function dispatchFunction(type, params) {
1932
+ return function() {
1933
+ return dispatchEvent(this, type, params.apply(this, arguments));
1934
+ };
1935
+ }
1936
+
1937
+ function selection_dispatch(type, params) {
1938
+ return this.each((typeof params === "function"
1939
+ ? dispatchFunction
1940
+ : dispatchConstant)(type, params));
1941
+ }
1942
+
1943
+ var root = [null];
1944
+
1945
+ function Selection(groups, parents) {
1946
+ this._groups = groups;
1947
+ this._parents = parents;
1948
+ }
1949
+
1950
+ Selection.prototype = {
1951
+ constructor: Selection,
1952
+ select: selection_select,
1953
+ selectAll: selection_selectAll,
1954
+ filter: selection_filter,
1955
+ data: selection_data,
1956
+ enter: selection_enter,
1957
+ exit: selection_exit,
1958
+ join: selection_join,
1959
+ merge: selection_merge,
1960
+ order: selection_order,
1961
+ sort: selection_sort,
1962
+ call: selection_call,
1963
+ nodes: selection_nodes,
1964
+ node: selection_node,
1965
+ size: selection_size,
1966
+ empty: selection_empty,
1967
+ each: selection_each,
1968
+ attr: selection_attr,
1969
+ style: selection_style,
1970
+ property: selection_property,
1971
+ classed: selection_classed,
1972
+ text: selection_text,
1973
+ html: selection_html,
1974
+ raise: selection_raise,
1975
+ lower: selection_lower,
1976
+ append: selection_append,
1977
+ insert: selection_insert,
1978
+ remove: selection_remove,
1979
+ clone: selection_clone,
1980
+ datum: selection_datum,
1981
+ on: selection_on,
1982
+ dispatch: selection_dispatch
1983
+ };
1984
+
1985
+ function select(selector) {
1986
+ return typeof selector === "string"
1987
+ ? new Selection([[document.querySelector(selector)]], [document.documentElement])
1988
+ : new Selection([[selector]], root);
1989
+ }
1990
+
1991
+ ///////////////////////////////////////////////////////////////////////////////
1992
+ /** @preserve
1993
+ ///// SAPC APCA - Advanced Perceptual Contrast Algorithm
1994
+ ///// Beta 0.1.9 W3 • contrast function only
1995
+ ///// DIST: W3 • Revision date: July 3, 2022
1996
+ ///// Function to parse color values and determine Lc contrast
1997
+ ///// Copyright © 2019-2022 by Andrew Somers. All Rights Reserved.
1998
+ ///// LICENSE: W3 LICENSE
1999
+ ///// CONTACT: Please use the ISSUES or DISCUSSIONS tab at:
2000
+ ///// https://github.com/Myndex/SAPC-APCA/
2001
+ /////
2002
+ ///////////////////////////////////////////////////////////////////////////////
2003
+ /////
2004
+ ///// MINIMAL IMPORTS:
2005
+ ///// import { APCAcontrast, sRGBtoY, displayP3toY,
2006
+ ///// calcAPCA, fontLookupAPCA } from 'apca-w3';
2007
+ ///// import { colorParsley } from 'colorparsley';
2008
+ /////
2009
+ ///// FORWARD CONTRAST USAGE:
2010
+ ///// Lc = APCAcontrast( sRGBtoY( TEXTcolor ) , sRGBtoY( BACKGNDcolor ) );
2011
+ ///// Where the colors are sent as an rgba array [255,255,255,1]
2012
+ /////
2013
+ ///// Retrieving an array of font sizes for the contrast:
2014
+ ///// fontArray = fontLookupAPCA(Lc);
2015
+ /////
2016
+ ///// Live Demonstrator at https://www.myndex.com/APCA/
2017
+ // */
2018
+ ///////////////////////////////////////////////////////////////////////////////
2019
+
2020
+ // */ //// END LOCAL TESTING SWITCH
2021
+
2022
+
2023
+ ///// Module Scope Object Containing Constants /////
2024
+ ///// APCA 0.0.98G - 4g - W3 Compatible Constants
2025
+
2026
+ ///// 𝒦 SA98G ///////////////////////////////////
2027
+ const SA98G = {
2028
+
2029
+ mainTRC: 2.4, // 2.4 exponent for emulating actual monitor perception
2030
+
2031
+ // sRGB coefficients
2032
+ sRco: 0.2126729,
2033
+ sGco: 0.7151522,
2034
+ sBco: 0.0721750,
2035
+
2036
+ // G-4g constants for use with 2.4 exponent
2037
+ normBG: 0.56,
2038
+ normTXT: 0.57,
2039
+ revTXT: 0.62,
2040
+ revBG: 0.65,
2041
+
2042
+ // G-4g Clamps and Scalers
2043
+ blkThrs: 0.022,
2044
+ blkClmp: 1.414,
2045
+ scaleBoW: 1.14,
2046
+ scaleWoB: 1.14,
2047
+ loBoWoffset: 0.027,
2048
+ loWoBoffset: 0.027,
2049
+ deltaYmin: 0.0005,
2050
+ loClip: 0.1};
2051
+
2052
+
2053
+
2054
+
2055
+ //////////////////////////////////////////////////////////////////////////////
2056
+ ////////// APCA CALCULATION FUNCTIONS \/////////////////////////////////////
2057
+
2058
+ ////////// ƒ APCAcontrast() ////////////////////////////////////////////
2059
+ function APCAcontrast (txtY,bgY,places = -1) {
2060
+ // send linear Y (luminance) for text and background.
2061
+ // txtY and bgY must be between 0.0-1.0
2062
+ // IMPORTANT: Do not swap, polarity is important.
2063
+
2064
+ const icp = [0.0,1.1]; // input range clamp / input error check
2065
+
2066
+ if(isNaN(txtY)||isNaN(bgY)||Math.min(txtY,bgY)<icp[0]||
2067
+ Math.max(txtY,bgY)>icp[1]){
2068
+ return 0.0; // return zero on error
2069
+ // return 'error'; // optional string return for error
2070
+ }
2071
+ ////////// SAPC LOCAL VARS /////////////////////////////////////////
2072
+
2073
+ let SAPC = 0.0; // For raw SAPC values
2074
+ let outputContrast = 0.0; // For weighted final values
2075
+ let polCat = 'BoW'; // Alternate Polarity Indicator. N normal R reverse
2076
+
2077
+ // TUTORIAL
2078
+
2079
+ // Use Y for text and BG, and soft clamp black,
2080
+ // return 0 for very close luminances, determine
2081
+ // polarity, and calculate SAPC raw contrast
2082
+ // Then scale for easy to remember levels.
2083
+
2084
+ // Note that reverse contrast (white text on black)
2085
+ // intentionally returns a negative number
2086
+ // Proper polarity is important!
2087
+
2088
+ ////////// BLACK SOFT CLAMP ////////////////////////////////////////
2089
+
2090
+ // Soft clamps Y for either color if it is near black.
2091
+ txtY = (txtY > SA98G.blkThrs) ? txtY :
2092
+ txtY + Math.pow(SA98G.blkThrs - txtY, SA98G.blkClmp);
2093
+ bgY = (bgY > SA98G.blkThrs) ? bgY :
2094
+ bgY + Math.pow(SA98G.blkThrs - bgY, SA98G.blkClmp);
2095
+
2096
+ ///// Return 0 Early for extremely low ∆Y
2097
+ if ( Math.abs(bgY - txtY) < SA98G.deltaYmin ) { return 0.0; }
2098
+
2099
+
2100
+ ////////// APCA/SAPC CONTRAST - LOW CLIP (W3 LICENSE) ///////////////
2101
+
2102
+ if ( bgY > txtY ) { // For normal polarity, black text on white (BoW)
2103
+
2104
+ // Calculate the SAPC contrast value and scale
2105
+ SAPC = ( Math.pow(bgY, SA98G.normBG) -
2106
+ Math.pow(txtY, SA98G.normTXT) ) * SA98G.scaleBoW;
2107
+
2108
+ // Low Contrast smooth rollout to prevent polarity reversal
2109
+ // and also a low-clip for very low contrasts
2110
+ outputContrast = (SAPC < SA98G.loClip) ? 0.0 : SAPC - SA98G.loBoWoffset;
2111
+
2112
+ } else { // For reverse polarity, light text on dark (WoB)
2113
+ // WoB should always return negative value.
2114
+ polCat = 'WoB';
2115
+
2116
+ SAPC = ( Math.pow(bgY, SA98G.revBG) -
2117
+ Math.pow(txtY, SA98G.revTXT) ) * SA98G.scaleWoB;
2118
+
2119
+ outputContrast = (SAPC > -0.1) ? 0.0 : SAPC + SA98G.loWoBoffset;
2120
+ }
2121
+
2122
+ // return Lc (lightness contrast) as a signed numeric value
2123
+ // Round to the nearest whole number as string is optional.
2124
+ // Rounded can be a signed INT as output will be within ± 127
2125
+ // places = -1 returns signed float, 1 or more set that many places
2126
+ // 0 returns rounded string, uses BoW or WoB instead of minus sign
2127
+
2128
+ if(places < 0 ){ // Default (-1) number out, all others are strings
2129
+ return outputContrast * 100.0;
2130
+ } else if(places == 0 ){
2131
+ return Math.round(Math.abs(outputContrast)*100.0)+'<sub>'+polCat+'</sub>';
2132
+ } else if(Number.isInteger(places)){
2133
+ return (outputContrast * 100.0).toFixed(places);
2134
+ } else { return 0.0 }
2135
+
2136
+ } // End APCAcontrast()
2137
+
2138
+ /////////\ ///////////////////////////\
2139
+ //////////\ END fontLookupAPCA() 0.1.7 (G) /////////////////////////////\
2140
+ /////////////////////////////////////////////////////////////////////////////\
2141
+
2142
+
2143
+
2144
+
2145
+ //////////////////////////////////////////////////////////////////////////////
2146
+ ////////// LUMINANCE CONVERTERS |//////////////////////////////////////////
2147
+
2148
+
2149
+ ////////// ƒ sRGBtoY() //////////////////////////////////////////////////
2150
+ function sRGBtoY (rgb = [0,0,0]) { // send sRGB 8bpc (0xFFFFFF) or string
2151
+
2152
+ // NOTE: Currently expects 0-255
2153
+
2154
+ ///// APCA 0.0.98G - 4g - W3 Compatible Constants ////////////////////
2155
+ /*
2156
+ const mainTRC = 2.4; // 2.4 exponent emulates actual monitor perception
2157
+
2158
+ const sRco = 0.2126729,
2159
+ sGco = 0.7151522,
2160
+ sBco = 0.0721750; // sRGB coefficients
2161
+ */
2162
+ // Future:
2163
+ // 0.2126478133913640 0.7151791475336150 0.0721730390750208
2164
+ // Derived from:
2165
+ // xW yW K xR yR xG yG xB yB
2166
+ // 0.312720 0.329030 6504 0.640 0.330 0.300 0.600 0.150 0.060
2167
+
2168
+ // linearize r, g, or b then apply coefficients
2169
+ // and sum then return the resulting luminance
2170
+
2171
+ function simpleExp (chan) { return Math.pow(chan/255.0, SA98G.mainTRC); }
2172
+ return SA98G.sRco * simpleExp(rgb[0]) +
2173
+ SA98G.sGco * simpleExp(rgb[1]) +
2174
+ SA98G.sBco * simpleExp(rgb[2]);
2175
+
2176
+ } // End sRGBtoY()
2177
+
2178
+
2179
+
2180
+
2181
+ //\ ////////////////////////////////////////
2182
+ ///\ ////////////////////////////////////////
2183
+ ////\ ////////////////////////////////////////
2184
+ /////\ END APCA 0.1.9 G-4g BLOCK ////////////////////////////////////////
2185
+ ////////////////////////////////////////////////////////////////////////////
2186
+ ///////////////////////////////////////////////////////////////////////////
2187
+
2188
+ function hexToColor(hex_string, opacity) {
2189
+ if (typeof hex_string != "string") return false;
2190
+ var c = color(hex_string);
2191
+ c.opacity = 1;
2192
+
2193
+ return c;
2194
+ }
2195
+
2196
+ function getAPCAContrast(text_hex, background_hex) {
2197
+ if (!text_hex || !background_hex) {
2198
+ console.warn("No valid color", text_hex, background_hex);
2199
+ return;
2200
+ }
2201
+ const text_y = sRGBtoY(Object.values(hexToColor(text_hex)));
2202
+ const background_y = sRGBtoY(Object.values(hexToColor(background_hex)));
2203
+ return Math.abs(APCAcontrast(text_y, background_y));
2204
+ }
2205
+
2206
+ function isPaleBackground(background_hex) {
2207
+ if (!background_hex) {
2208
+ console.warn("No valid color", background_hex);
2209
+ return;
2210
+ }
2211
+
2212
+ const black_contrast = getAPCAContrast("#000000", background_hex);
2213
+ const white_contrast = getAPCAContrast("#ffffff", background_hex);
2214
+ return white_contrast < black_contrast ? true : false;
2215
+ }
2216
+
2217
+ const DEFAULTS = Object.freeze({
2218
+ searchbox_input_expanded: false,
2219
+ searchbox_size: 2,
2220
+ searchbox_width: 12,
2221
+ search_bg_color: null,
2222
+ search_bg_opacity: 0.9,
2223
+ searchbox_stroke: 0.1,
2224
+ searchbox_color: null,
2225
+ searchbox_radius: 4,
2226
+ });
2227
+
2228
+ function SearchboxStyle(state_, selector_, layout_) {
2229
+ this._state = state_;
2230
+ for (const key in DEFAULTS) {
2231
+ if (this._state[key] === undefined) this._state[key] = DEFAULTS[key];
2232
+ }
2233
+
2234
+ this._layout = layout_ || {};
2235
+
2236
+ this._styles = new Stylesheet();
2237
+ this._selector = selector_;
2238
+ this._createStylesheet();
2239
+
2240
+ return this;
2241
+ }
2242
+
2243
+ SearchboxStyle.prototype._createStylesheet = function () {
2244
+ this._stylesheet = document.createElement("style");
2245
+ this._stylesheet.className = "fl-ui-styles-searchbox";
2246
+ document.head.appendChild(this._stylesheet);
2247
+ };
2248
+
2249
+ SearchboxStyle.prototype.update = function () {
2250
+ this._styles.clear();
2251
+ const prefers_reduced_motion =
2252
+ window.matchMedia &&
2253
+ window.matchMedia("(prefers-reduced-motion: reduce)").matches;
2254
+
2255
+ const bg_color = this._state.search_bg_color || this._layout.background_color;
2256
+ const background_color = bg_color
2257
+ ? hexToRgba(bg_color, this._state.search_bg_opacity)
2258
+ : "transparent";
2259
+
2260
+ const user_defined_searchbox_color =
2261
+ this._state.searchbox_color || this._layout.font_color;
2262
+ const searchbox_color = user_defined_searchbox_color || "inherit";
2263
+ const searchbox_color_paler = user_defined_searchbox_color
2264
+ ? hexToRgba(
2265
+ user_defined_searchbox_color,
2266
+ (this._state.searchbox_stroke + 1) / 2,
2267
+ )
2268
+ : "currentColor";
2269
+ const searchbox_color_palest = user_defined_searchbox_color
2270
+ ? hexToRgba(user_defined_searchbox_color, this._state.searchbox_stroke)
2271
+ : "currentColor";
2272
+
2273
+ const TEXT_SIZE = 0.55; // Relative to the search box height
2274
+ const ICON_SIZE = 0.45; // Relative to the search box height
2275
+
2276
+ // Main container
2277
+ this._styles
2278
+ .select(this._selector)
2279
+ .style("display", "inline-block")
2280
+ .style(
2281
+ "width",
2282
+ this._state.searchbox_size + this._state.searchbox_width + "rem",
2283
+ )
2284
+ .style("max-width", "calc(100% - 2px)")
2285
+ .style("box-sizing", "border-box");
2286
+
2287
+ select(this._selector).attr(
2288
+ "data-pale-background",
2289
+ isPaleBackground(background_color),
2290
+ );
2291
+
2292
+ // Input field
2293
+ this._styles
2294
+ .select(this._selector + " input")
2295
+ .style("border", "2px solid " + searchbox_color_palest)
2296
+ .style("border-radius", this._state.searchbox_radius + "px")
2297
+ .style("background-color", background_color)
2298
+ .style("color", searchbox_color)
2299
+ .style("font-family", "inherit")
2300
+ .style("cursor", this._state.searchbox_input_expanded ? "text" : "pointer")
2301
+ .style("position", "relative")
2302
+ .style("opacity", this._state.search_bg_opacity)
2303
+ .style("transition", prefers_reduced_motion ? "none" : "200ms width")
2304
+ .style("display", "block")
2305
+ .style("box-sizing", "border-box")
2306
+ .style("padding", "0 0 0 calc(" + this._state.searchbox_size + "rem - 4px)")
2307
+ .style("font-size", this._state.searchbox_size * TEXT_SIZE + "rem");
2308
+
2309
+ this._styles
2310
+ .select(this._selector + ".expanded input")
2311
+ .style("cursor", "text");
2312
+
2313
+ if (this._state.searchbox_input_expanded) {
2314
+ this._styles
2315
+ .select(this._selector + " input")
2316
+ .style("width", "100%")
2317
+ .style("height", this._state.searchbox_size + "rem");
2318
+ } else {
2319
+ this._styles
2320
+ .select(this._selector + " input")
2321
+ .style("width", this._state.searchbox_size + "rem")
2322
+ .style("height", this._state.searchbox_size + "rem");
2323
+ }
2324
+
2325
+ // Search icon
2326
+ this._styles
2327
+ .select(this._selector + " .search-icon")
2328
+ .style("position", "absolute")
2329
+ .style("pointer-events", "none")
2330
+ .style("width", this._state.searchbox_size * ICON_SIZE + "rem")
2331
+ .style("height", this._state.searchbox_size * ICON_SIZE + "rem")
2332
+ .style(
2333
+ "left",
2334
+ this._state.searchbox_size * Math.pow(1 - ICON_SIZE, 2) + "rem",
2335
+ )
2336
+ .style(
2337
+ "top",
2338
+ this._state.searchbox_size * Math.pow(1 - ICON_SIZE, 2) + "rem",
2339
+ );
2340
+
2341
+ // Search icon SVG
2342
+ this._styles
2343
+ .select(this._selector + " .search-icon svg")
2344
+ .style("width", "100%")
2345
+ .style("height", "100%")
2346
+ .select("path")
2347
+ .style("fill", searchbox_color);
2348
+
2349
+ // Search list (dropdown)
2350
+ const list_bg_color =
2351
+ this._state.search_bg_color || this._layout.background_color;
2352
+ this._styles
2353
+ .select(this._selector + " .search-list")
2354
+ .style("display", "none")
2355
+ .style("opacity", "0")
2356
+ .style("margin-top", "1px")
2357
+ .style("min-width", "100%")
2358
+ .style("max-height", "50vh")
2359
+ .style("overflow", "auto")
2360
+ .style("position", "absolute")
2361
+ .style("box-shadow", "0 1px 3px rgba(0,0,0,0.2)")
2362
+ .style("color", searchbox_color)
2363
+ .style("border-radius", "3px")
2364
+ .style("font-size", this._state.searchbox_size * TEXT_SIZE + "rem")
2365
+ .style("z-index", "1000");
2366
+
2367
+ this._styles
2368
+ .select(this._selector + " .search-list.visible")
2369
+ .style("opacity", 1)
2370
+ .style("visibility", "visible")
2371
+ .style("pointer-events", "auto");
2372
+
2373
+ // Apply data-pale-background to the search-list for accessibility styles
2374
+ select(this._selector + " .search-list").attr(
2375
+ "data-pale-background",
2376
+ isPaleBackground(list_bg_color || background_color),
2377
+ );
2378
+
2379
+ // Search list items
2380
+ this._styles
2381
+ .select(this._selector + " .search-list .search-item")
2382
+ .style("height", this._state.searchbox_size * TEXT_SIZE + 1 + "rem")
2383
+ .style("background-color", list_bg_color || "transparent")
2384
+ .style("opacity", this._state.search_bg_opacity)
2385
+ .style("padding", "0.5rem")
2386
+ .style("cursor", "pointer")
2387
+ .style("position", "relative")
2388
+ .select("p")
2389
+ .style("margin", "0")
2390
+ .style("line-height", "1.2em")
2391
+ .style("display", "block")
2392
+ .style("white-space", "nowrap")
2393
+ .style("font-weight", "normal")
2394
+ .style("overflow", "hidden")
2395
+ .style("text-overflow", "ellipsis")
2396
+ .style("pointer-events", "none");
2397
+
2398
+ // Search list item hover and selected states for pale backgrounds
2399
+ this._styles
2400
+ .select(
2401
+ this._selector +
2402
+ " .search-list[data-pale-background='true'] .search-item:hover",
2403
+ )
2404
+ .style("outline", "2px solid black")
2405
+ .style("outline-offset", "-2px");
2406
+
2407
+ this._styles
2408
+ .select(
2409
+ this._selector +
2410
+ " .search-list[data-pale-background='true'] .search-item.selected",
2411
+ )
2412
+ .style("outline", "2px solid black")
2413
+ .style("outline-offset", "-2px")
2414
+ .style("cursor", "default");
2415
+
2416
+ this._styles
2417
+ .select(
2418
+ this._selector +
2419
+ " .search-list[data-pale-background='true'] .search-item:focus-visible",
2420
+ )
2421
+ .style("outline", "2px solid black")
2422
+ .style("outline-offset", "-2px");
2423
+
2424
+ // Search list item hover and selected states for dark backgrounds
2425
+ this._styles
2426
+ .select(
2427
+ this._selector +
2428
+ " .search-list[data-pale-background='false'] .search-item:hover",
2429
+ )
2430
+ .style("outline", "2px solid white")
2431
+ .style("outline-offset", "-2px");
2432
+
2433
+ this._styles
2434
+ .select(
2435
+ this._selector +
2436
+ " .search-list[data-pale-background='false'] .search-item.selected",
2437
+ )
2438
+ .style("outline", "2px solid white")
2439
+ .style("outline-offset", "-2px")
2440
+ .style("cursor", "default");
2441
+
2442
+ this._styles
2443
+ .select(
2444
+ this._selector +
2445
+ " .search-list[data-pale-background='false'] .search-item:focus-visible",
2446
+ )
2447
+ .style("outline", "2px solid white")
2448
+ .style("outline-offset", "-2px");
2449
+
2450
+ // Active state (when search is focused)
2451
+ this._styles
2452
+ .select(this._selector + ".active .search-list")
2453
+ .style("display", "block");
2454
+
2455
+ // Expanded state
2456
+ this._styles
2457
+ .select(this._selector + ".expanded input")
2458
+ .style("width", "100% !important");
2459
+
2460
+ this._styles
2461
+ .select(this._selector + " input::placeholder")
2462
+ .style("color", searchbox_color_paler);
2463
+
2464
+ this._stylesheet.innerHTML = this._styles.print();
2465
+ };
2466
+
2467
+ /**
2468
+ * @typedef {import('./searchbox-style/index.js').SearchboxStyleState} SearchboxStyleState
2469
+ */
2470
+
1047
2471
  function createGeneralControlsStyle(state, selector, layout) {
1048
2472
  return new GeneralControlsStyle(state, selector, layout);
1049
2473
  }
@@ -1060,9 +2484,14 @@
1060
2484
  return new SliderStyle(state, selector, layout);
1061
2485
  }
1062
2486
 
2487
+ function createSearchboxStyle(state, selector, layout) {
2488
+ return new SearchboxStyle(state, selector, layout);
2489
+ }
2490
+
1063
2491
  exports.createButtonStyle = createButtonStyle;
1064
2492
  exports.createDropdownStyle = createDropdownStyle;
1065
2493
  exports.createGeneralControlsStyle = createGeneralControlsStyle;
2494
+ exports.createSearchboxStyle = createSearchboxStyle;
1066
2495
  exports.createSliderStyle = createSliderStyle;
1067
2496
 
1068
2497
  }));