@uwdata/mosaic-plot 0.12.1 → 0.13.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/dist/mosaic-plot.js +340 -239
- package/dist/mosaic-plot.min.js +8 -8
- package/package.json +9 -12
- package/src/interactors/Interval1D.js +4 -0
- package/src/interactors/Interval2D.js +4 -0
- package/src/interactors/Nearest.js +10 -4
- package/src/interactors/PanZoom.js +17 -10
- package/src/interactors/Region.js +4 -0
- package/src/interactors/Toggle.js +10 -3
- package/src/marks/ConnectedMark.js +1 -1
- package/src/marks/Density1DMark.js +68 -21
- package/src/marks/Mark.js +8 -4
- package/src/marks/util/grid.js +36 -7
- package/src/marks/util/is-constant-option.js +2 -0
- package/src/plot.js +14 -3
- package/vitest.config.ts +3 -0
package/dist/mosaic-plot.js
CHANGED
|
@@ -103,7 +103,7 @@ var require_search_bounds = __commonJS({
|
|
|
103
103
|
}
|
|
104
104
|
return -1;
|
|
105
105
|
}
|
|
106
|
-
function
|
|
106
|
+
function norm2(a2, y3, c4, l, h, f) {
|
|
107
107
|
if (typeof c4 === "function") {
|
|
108
108
|
return f(a2, y3, c4, l === void 0 ? 0 : l | 0, h === void 0 ? a2.length - 1 : h | 0);
|
|
109
109
|
}
|
|
@@ -111,19 +111,19 @@ var require_search_bounds = __commonJS({
|
|
|
111
111
|
}
|
|
112
112
|
module.exports = {
|
|
113
113
|
ge: function(a2, y3, c4, l, h) {
|
|
114
|
-
return
|
|
114
|
+
return norm2(a2, y3, c4, l, h, ge);
|
|
115
115
|
},
|
|
116
116
|
gt: function(a2, y3, c4, l, h) {
|
|
117
|
-
return
|
|
117
|
+
return norm2(a2, y3, c4, l, h, gt2);
|
|
118
118
|
},
|
|
119
119
|
lt: function(a2, y3, c4, l, h) {
|
|
120
|
-
return
|
|
120
|
+
return norm2(a2, y3, c4, l, h, lt2);
|
|
121
121
|
},
|
|
122
122
|
le: function(a2, y3, c4, l, h) {
|
|
123
|
-
return
|
|
123
|
+
return norm2(a2, y3, c4, l, h, le);
|
|
124
124
|
},
|
|
125
125
|
eq: function(a2, y3, c4, l, h) {
|
|
126
|
-
return
|
|
126
|
+
return norm2(a2, y3, c4, l, h, eq2);
|
|
127
127
|
}
|
|
128
128
|
};
|
|
129
129
|
}
|
|
@@ -489,168 +489,9 @@ var Fixed = Symbol("Fixed");
|
|
|
489
489
|
var Transient = Symbol("Transient");
|
|
490
490
|
var Transform = Symbol("Transform");
|
|
491
491
|
|
|
492
|
-
// ../core/src/util/throttle.js
|
|
493
|
-
var NIL = {};
|
|
494
|
-
function throttle(callback, debounce = false) {
|
|
495
|
-
let curr;
|
|
496
|
-
let next;
|
|
497
|
-
let pending = NIL;
|
|
498
|
-
function invoke(event) {
|
|
499
|
-
curr = callback(event).catch(() => {
|
|
500
|
-
}).finally(() => {
|
|
501
|
-
if (next) {
|
|
502
|
-
const { value } = next;
|
|
503
|
-
next = null;
|
|
504
|
-
invoke(value);
|
|
505
|
-
} else {
|
|
506
|
-
curr = null;
|
|
507
|
-
}
|
|
508
|
-
});
|
|
509
|
-
}
|
|
510
|
-
function enqueue(event) {
|
|
511
|
-
next = { event };
|
|
512
|
-
}
|
|
513
|
-
function process(event) {
|
|
514
|
-
curr ? enqueue(event) : invoke(event);
|
|
515
|
-
}
|
|
516
|
-
function delay(event) {
|
|
517
|
-
if (pending !== event) {
|
|
518
|
-
requestAnimationFrame(() => {
|
|
519
|
-
const e = pending;
|
|
520
|
-
pending = NIL;
|
|
521
|
-
process(e);
|
|
522
|
-
});
|
|
523
|
-
}
|
|
524
|
-
pending = event;
|
|
525
|
-
}
|
|
526
|
-
return debounce ? delay : process;
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
// ../core/src/MosaicClient.js
|
|
530
|
-
var MosaicClient = class {
|
|
531
|
-
/**
|
|
532
|
-
* Constructor.
|
|
533
|
-
* @param {*} filterSelection An optional selection to interactively filter
|
|
534
|
-
* this client's data. If provided, a coordinator will re-query and update
|
|
535
|
-
* the client when the selection updates.
|
|
536
|
-
*/
|
|
537
|
-
constructor(filterSelection) {
|
|
538
|
-
this._filterBy = filterSelection;
|
|
539
|
-
this._requestUpdate = throttle(() => this.requestQuery(), true);
|
|
540
|
-
this._coordinator = null;
|
|
541
|
-
}
|
|
542
|
-
/**
|
|
543
|
-
* Return this client's connected coordinator.
|
|
544
|
-
*/
|
|
545
|
-
get coordinator() {
|
|
546
|
-
return this._coordinator;
|
|
547
|
-
}
|
|
548
|
-
/**
|
|
549
|
-
* Set this client's connected coordinator.
|
|
550
|
-
*/
|
|
551
|
-
set coordinator(coordinator2) {
|
|
552
|
-
this._coordinator = coordinator2;
|
|
553
|
-
}
|
|
554
|
-
/**
|
|
555
|
-
* Return this client's filter selection.
|
|
556
|
-
*/
|
|
557
|
-
get filterBy() {
|
|
558
|
-
return this._filterBy;
|
|
559
|
-
}
|
|
560
|
-
/**
|
|
561
|
-
* Return a boolean indicating if the client query can be sped up with
|
|
562
|
-
* materialized views of pre-aggregated data. Should return true if changes to
|
|
563
|
-
* the filterBy selection does not change the groupby domain of the client
|
|
564
|
-
* query.
|
|
565
|
-
*/
|
|
566
|
-
get filterStable() {
|
|
567
|
-
return true;
|
|
568
|
-
}
|
|
569
|
-
/**
|
|
570
|
-
* Return an array of fields queried by this client.
|
|
571
|
-
* @returns {object[]|null} The fields to retrieve info for.
|
|
572
|
-
*/
|
|
573
|
-
fields() {
|
|
574
|
-
return null;
|
|
575
|
-
}
|
|
576
|
-
/**
|
|
577
|
-
* Called by the coordinator to set the field info for this client.
|
|
578
|
-
* @param {*} info The field info result.
|
|
579
|
-
* @returns {this}
|
|
580
|
-
*/
|
|
581
|
-
fieldInfo(info) {
|
|
582
|
-
return this;
|
|
583
|
-
}
|
|
584
|
-
/**
|
|
585
|
-
* Return a query specifying the data needed by this client.
|
|
586
|
-
* @param {*} [filter] The filtering criteria to apply in the query.
|
|
587
|
-
* @returns {*} The client query
|
|
588
|
-
*/
|
|
589
|
-
query(filter3) {
|
|
590
|
-
return null;
|
|
591
|
-
}
|
|
592
|
-
/**
|
|
593
|
-
* Called by the coordinator to inform the client that a query is pending.
|
|
594
|
-
* @returns {this}
|
|
595
|
-
*/
|
|
596
|
-
queryPending() {
|
|
597
|
-
return this;
|
|
598
|
-
}
|
|
599
|
-
/**
|
|
600
|
-
* Called by the coordinator to return a query result.
|
|
601
|
-
* @param {*} data The query result.
|
|
602
|
-
* @returns {this}
|
|
603
|
-
*/
|
|
604
|
-
queryResult(data) {
|
|
605
|
-
return this;
|
|
606
|
-
}
|
|
607
|
-
/**
|
|
608
|
-
* Called by the coordinator to report a query execution error.
|
|
609
|
-
* @param {*} error
|
|
610
|
-
* @returns {this}
|
|
611
|
-
*/
|
|
612
|
-
queryError(error) {
|
|
613
|
-
return this;
|
|
614
|
-
}
|
|
615
|
-
/**
|
|
616
|
-
* Request the coordinator to execute a query for this client.
|
|
617
|
-
* If an explicit query is not provided, the client query method will
|
|
618
|
-
* be called, filtered by the current filterBy selection.
|
|
619
|
-
* @returns {Promise}
|
|
620
|
-
*/
|
|
621
|
-
requestQuery(query) {
|
|
622
|
-
const q = query || this.query(this.filterBy?.predicate(this));
|
|
623
|
-
return this._coordinator.requestQuery(this, q);
|
|
624
|
-
}
|
|
625
|
-
/**
|
|
626
|
-
* Request that the coordinator perform a throttled update of this client
|
|
627
|
-
* using the default query. Unlike requestQuery, for which every call will
|
|
628
|
-
* result in an executed query, multiple calls to requestUpdate may be
|
|
629
|
-
* consolidated into a single update.
|
|
630
|
-
*/
|
|
631
|
-
requestUpdate() {
|
|
632
|
-
this._requestUpdate();
|
|
633
|
-
}
|
|
634
|
-
/**
|
|
635
|
-
* Reset this client, initiating new field info and query requests.
|
|
636
|
-
* @returns {Promise}
|
|
637
|
-
*/
|
|
638
|
-
initialize() {
|
|
639
|
-
return this._coordinator.initializeClient(this);
|
|
640
|
-
}
|
|
641
|
-
/**
|
|
642
|
-
* Requests a client update.
|
|
643
|
-
* For example to (re-)render an interface component.
|
|
644
|
-
*
|
|
645
|
-
* @returns {this | Promise<any>}
|
|
646
|
-
*/
|
|
647
|
-
update() {
|
|
648
|
-
return this;
|
|
649
|
-
}
|
|
650
|
-
};
|
|
651
|
-
|
|
652
492
|
// ../../node_modules/@uwdata/flechette/src/constants.js
|
|
653
493
|
var MAGIC = Uint8Array.of(65, 82, 82, 79, 87, 49);
|
|
494
|
+
var EOS = Uint8Array.of(255, 255, 255, 255, 0, 0, 0, 0);
|
|
654
495
|
var Version = (
|
|
655
496
|
/** @type {const} */
|
|
656
497
|
{
|
|
@@ -3065,7 +2906,7 @@ function literalToSQL(value) {
|
|
|
3065
2906
|
case "number":
|
|
3066
2907
|
return Number.isFinite(value) ? `${value}` : "NULL";
|
|
3067
2908
|
case "string":
|
|
3068
|
-
return `'${value.
|
|
2909
|
+
return `'${value.replaceAll(`'`, `''`)}'`;
|
|
3069
2910
|
case "boolean":
|
|
3070
2911
|
return value ? "TRUE" : "FALSE";
|
|
3071
2912
|
default:
|
|
@@ -3456,6 +3297,9 @@ var ColumnNameRefNode = class extends ColumnRefNode {
|
|
|
3456
3297
|
};
|
|
3457
3298
|
|
|
3458
3299
|
// ../sql/src/ast/column-param.js
|
|
3300
|
+
function isColumnParam(value) {
|
|
3301
|
+
return value instanceof ColumnParamNode;
|
|
3302
|
+
}
|
|
3459
3303
|
var ColumnParamNode = class extends ColumnRefNode {
|
|
3460
3304
|
/**
|
|
3461
3305
|
* Instantiate a column param node.
|
|
@@ -4877,7 +4721,7 @@ function collectAggregates(root2) {
|
|
|
4877
4721
|
function collectColumns(root2) {
|
|
4878
4722
|
const cols = {};
|
|
4879
4723
|
walk(root2, (node) => {
|
|
4880
|
-
if (node.type === COLUMN_REF) {
|
|
4724
|
+
if (node.type === COLUMN_REF || node.type === COLUMN_PARAM) {
|
|
4881
4725
|
cols[node] = node;
|
|
4882
4726
|
}
|
|
4883
4727
|
});
|
|
@@ -4921,21 +4765,21 @@ function bin2d(q, xp, yp, aggs, xn, groupby) {
|
|
|
4921
4765
|
}
|
|
4922
4766
|
|
|
4923
4767
|
// ../sql/src/transforms/bin-linear-1d.js
|
|
4924
|
-
function binLinear1d(query, x3, weight) {
|
|
4768
|
+
function binLinear1d(query, x3, weight = void 0, groupby = []) {
|
|
4925
4769
|
const w = weight ? (x4) => mul(x4, weight) : (x4) => x4;
|
|
4926
4770
|
const p0 = floor(x3);
|
|
4927
4771
|
const p1 = add(p0, 1);
|
|
4928
4772
|
return Query.from(Query.unionAll(
|
|
4929
4773
|
query.clone().select({ i: int322(p0), w: w(sub(p1, x3)) }),
|
|
4930
4774
|
query.clone().select({ i: int322(p1), w: w(sub(x3, p0)) })
|
|
4931
|
-
)).select({ index: "i", density: sum("w") }).groupby("index").having(neq("density", 0));
|
|
4775
|
+
)).select({ index: "i", density: sum("w") }, groupby).groupby("index", groupby).having(neq("density", 0));
|
|
4932
4776
|
}
|
|
4933
4777
|
|
|
4934
4778
|
// ../sql/src/transforms/bin-linear-2d.js
|
|
4935
4779
|
function identity(x3) {
|
|
4936
4780
|
return x3;
|
|
4937
4781
|
}
|
|
4938
|
-
function binLinear2d(q, xp, yp, weight, xn, groupby) {
|
|
4782
|
+
function binLinear2d(q, xp, yp, weight, xn, groupby = []) {
|
|
4939
4783
|
const w = weight ? (x3) => mul(x3, weight) : identity;
|
|
4940
4784
|
const subq = (i, w2) => q.clone().select({ xp, yp, i, w: w2 });
|
|
4941
4785
|
const index2 = (x3, y3) => add(x3, mul(y3, xn));
|
|
@@ -5136,6 +4980,8 @@ function sufficientStatistics(node, preagg, avg2) {
|
|
|
5136
4980
|
return sumExpr(preagg, node);
|
|
5137
4981
|
case "avg":
|
|
5138
4982
|
return avgExpr(preagg, node);
|
|
4983
|
+
case "geomean":
|
|
4984
|
+
return geomeanExpr(preagg, node);
|
|
5139
4985
|
case "arg_max":
|
|
5140
4986
|
return argmaxExpr(preagg, node);
|
|
5141
4987
|
case "arg_min":
|
|
@@ -5219,6 +5065,12 @@ function avgExpr(preagg, node) {
|
|
|
5219
5065
|
const { expr, name } = countExpr(preagg, node);
|
|
5220
5066
|
return div(sum(mul(as, name)), expr);
|
|
5221
5067
|
}
|
|
5068
|
+
function geomeanExpr(preagg, node) {
|
|
5069
|
+
const x3 = node.args[0];
|
|
5070
|
+
const expr = addStat(preagg, sum(ln(x3)), node);
|
|
5071
|
+
const { expr: n } = countExpr(preagg, node);
|
|
5072
|
+
return exp(div(sum(expr), n));
|
|
5073
|
+
}
|
|
5222
5074
|
function argmaxExpr(preagg, node) {
|
|
5223
5075
|
const expr = addStat(preagg, node);
|
|
5224
5076
|
const maxy = addStat(preagg, max(node.args[1]), node);
|
|
@@ -5665,7 +5517,7 @@ var statMap = {
|
|
|
5665
5517
|
[Min]: min,
|
|
5666
5518
|
[Nulls]: (column3) => count().where(isNull(column3))
|
|
5667
5519
|
};
|
|
5668
|
-
function summarize(table, column3, stats) {
|
|
5520
|
+
function summarize({ table, column: column3, stats }) {
|
|
5669
5521
|
return Query.from(table).select(Array.from(stats, (s2) => ({ [s2]: statMap[s2](column3) })));
|
|
5670
5522
|
}
|
|
5671
5523
|
async function queryFieldInfo(mc, fields) {
|
|
@@ -5676,7 +5528,7 @@ async function queryFieldInfo(mc, fields) {
|
|
|
5676
5528
|
}
|
|
5677
5529
|
}
|
|
5678
5530
|
async function getFieldInfo(mc, { table, column: column3, stats }) {
|
|
5679
|
-
const q = Query.from({ source: table }).select({ column: column3 }).groupby(column3
|
|
5531
|
+
const q = Query.from({ source: table }).select({ column: column3 }).groupby(isNode(column3) && isAggregateExpression(column3) ? sql`ALL` : []);
|
|
5680
5532
|
const [desc2] = Array.from(await mc.query(Query.describe(q)));
|
|
5681
5533
|
const info = {
|
|
5682
5534
|
table,
|
|
@@ -5685,16 +5537,16 @@ async function getFieldInfo(mc, { table, column: column3, stats }) {
|
|
|
5685
5537
|
type: jsType(desc2.column_type),
|
|
5686
5538
|
nullable: desc2.null === "YES"
|
|
5687
5539
|
};
|
|
5688
|
-
if (!
|
|
5540
|
+
if (!stats?.length) return info;
|
|
5689
5541
|
const [result] = await mc.query(
|
|
5690
|
-
summarize(table, column3, stats),
|
|
5542
|
+
summarize({ table, column: column3, stats }),
|
|
5691
5543
|
{ persist: true }
|
|
5692
5544
|
);
|
|
5693
5545
|
return Object.assign(info, result);
|
|
5694
5546
|
}
|
|
5695
5547
|
async function getTableInfo(mc, table) {
|
|
5696
|
-
const result = await mc.query(`DESCRIBE ${asTableRef(table)}`);
|
|
5697
|
-
return
|
|
5548
|
+
const result = Array.from(await mc.query(`DESCRIBE ${asTableRef(table)}`));
|
|
5549
|
+
return result.map((desc2) => ({
|
|
5698
5550
|
table,
|
|
5699
5551
|
column: desc2.column_name,
|
|
5700
5552
|
sqlType: desc2.column_type,
|
|
@@ -6291,13 +6143,13 @@ var Coordinator = class {
|
|
|
6291
6143
|
}
|
|
6292
6144
|
/**
|
|
6293
6145
|
* Issue a query for which no result (return value) is needed.
|
|
6294
|
-
* @param {
|
|
6146
|
+
* @param { import('./types.js').QueryType[] |
|
|
6147
|
+
* import('./types.js').QueryType} query The query or an array of queries.
|
|
6295
6148
|
* Each query should be either a Query builder object or a SQL string.
|
|
6296
6149
|
* @param {object} [options] An options object.
|
|
6297
6150
|
* @param {number} [options.priority] The query priority, defaults to
|
|
6298
6151
|
* `Priority.Normal`.
|
|
6299
|
-
* @returns {QueryResult} A query result
|
|
6300
|
-
* promise.
|
|
6152
|
+
* @returns {QueryResult} A query result promise.
|
|
6301
6153
|
*/
|
|
6302
6154
|
exec(query, { priority = Priority.Normal } = {}) {
|
|
6303
6155
|
query = Array.isArray(query) ? query.filter((x3) => x3).join(";\n") : query;
|
|
@@ -6306,11 +6158,14 @@ var Coordinator = class {
|
|
|
6306
6158
|
/**
|
|
6307
6159
|
* Issue a query to the backing database. The submitted query may be
|
|
6308
6160
|
* consolidate with other queries and its results may be cached.
|
|
6309
|
-
* @param {QueryType} query The query as either a Query
|
|
6310
|
-
* or a SQL string.
|
|
6161
|
+
* @param {import('./types.js').QueryType} query The query as either a Query
|
|
6162
|
+
* builder object or a SQL string.
|
|
6311
6163
|
* @param {object} [options] An options object.
|
|
6312
6164
|
* @param {'arrow' | 'json'} [options.type] The query result format type.
|
|
6313
|
-
* @param {boolean} [options.cache=true] If true, cache the query result
|
|
6165
|
+
* @param {boolean} [options.cache=true] If true, cache the query result
|
|
6166
|
+
* client-side within the QueryManager.
|
|
6167
|
+
* @param {boolean} [options.persist] If true, request the database
|
|
6168
|
+
* server to persist a cached query server-side.
|
|
6314
6169
|
* @param {number} [options.priority] The query priority, defaults to
|
|
6315
6170
|
* `Priority.Normal`.
|
|
6316
6171
|
* @returns {QueryResult} A query result promise.
|
|
@@ -6326,8 +6181,8 @@ var Coordinator = class {
|
|
|
6326
6181
|
/**
|
|
6327
6182
|
* Issue a query to prefetch data for later use. The query result is cached
|
|
6328
6183
|
* for efficient future access.
|
|
6329
|
-
* @param {QueryType} query The query as either a Query
|
|
6330
|
-
* or a SQL string.
|
|
6184
|
+
* @param {import('./types.js').QueryType} query The query as either a Query
|
|
6185
|
+
* builder object or a SQL string.
|
|
6331
6186
|
* @param {object} [options] An options object.
|
|
6332
6187
|
* @param {'arrow' | 'json'} [options.type] The query result format type.
|
|
6333
6188
|
* @returns {QueryResult} A query result promise.
|
|
@@ -6362,13 +6217,13 @@ var Coordinator = class {
|
|
|
6362
6217
|
* Update client data by submitting the given query and returning the
|
|
6363
6218
|
* data (or error) to the client.
|
|
6364
6219
|
* @param {MosaicClient} client A Mosaic client.
|
|
6365
|
-
* @param {QueryType} query The data query.
|
|
6220
|
+
* @param {import('./types.js').QueryType} query The data query.
|
|
6366
6221
|
* @param {number} [priority] The query priority.
|
|
6367
6222
|
* @returns {Promise} A Promise that resolves upon completion of the update.
|
|
6368
6223
|
*/
|
|
6369
6224
|
updateClient(client, query, priority = Priority.Normal) {
|
|
6370
6225
|
client.queryPending();
|
|
6371
|
-
return this.query(query, { priority }).then(
|
|
6226
|
+
return client._pending = this.query(query, { priority }).then(
|
|
6372
6227
|
(data) => client.queryResult(data).update(),
|
|
6373
6228
|
(err) => {
|
|
6374
6229
|
this._logger.error(err);
|
|
@@ -6381,7 +6236,7 @@ var Coordinator = class {
|
|
|
6381
6236
|
* the client is simply updated. Otherwise `updateClient` is called. As a
|
|
6382
6237
|
* side effect, this method clears the current preaggregator state.
|
|
6383
6238
|
* @param {MosaicClient} client The client to update.
|
|
6384
|
-
* @param {QueryType | null} [query] The query to issue.
|
|
6239
|
+
* @param {import('./types.js').QueryType | null} [query] The query to issue.
|
|
6385
6240
|
*/
|
|
6386
6241
|
requestQuery(client, query) {
|
|
6387
6242
|
this.preaggregator.clear();
|
|
@@ -6398,7 +6253,7 @@ var Coordinator = class {
|
|
|
6398
6253
|
}
|
|
6399
6254
|
clients.add(client);
|
|
6400
6255
|
client.coordinator = this;
|
|
6401
|
-
this.initializeClient(client);
|
|
6256
|
+
client._pending = this.initializeClient(client);
|
|
6402
6257
|
connectSelection(this, client.filterBy, client);
|
|
6403
6258
|
}
|
|
6404
6259
|
async initializeClient(client) {
|
|
@@ -6406,6 +6261,7 @@ var Coordinator = class {
|
|
|
6406
6261
|
if (fields?.length) {
|
|
6407
6262
|
client.fieldInfo(await queryFieldInfo(this, fields));
|
|
6408
6263
|
}
|
|
6264
|
+
await client.prepare();
|
|
6409
6265
|
return client.requestQuery();
|
|
6410
6266
|
}
|
|
6411
6267
|
/**
|
|
@@ -7025,6 +6881,179 @@ var SelectionResolver = class {
|
|
|
7025
6881
|
}
|
|
7026
6882
|
};
|
|
7027
6883
|
|
|
6884
|
+
// ../core/src/util/throttle.js
|
|
6885
|
+
var NIL = {};
|
|
6886
|
+
function throttle(callback, debounce = false) {
|
|
6887
|
+
let curr;
|
|
6888
|
+
let next;
|
|
6889
|
+
let pending = NIL;
|
|
6890
|
+
function invoke(event) {
|
|
6891
|
+
curr = callback(event).catch(() => {
|
|
6892
|
+
}).finally(() => {
|
|
6893
|
+
if (next) {
|
|
6894
|
+
const { value } = next;
|
|
6895
|
+
next = null;
|
|
6896
|
+
invoke(value);
|
|
6897
|
+
} else {
|
|
6898
|
+
curr = null;
|
|
6899
|
+
}
|
|
6900
|
+
});
|
|
6901
|
+
}
|
|
6902
|
+
function enqueue(event) {
|
|
6903
|
+
next = { event };
|
|
6904
|
+
}
|
|
6905
|
+
function process(event) {
|
|
6906
|
+
curr ? enqueue(event) : invoke(event);
|
|
6907
|
+
}
|
|
6908
|
+
function delay(event) {
|
|
6909
|
+
if (pending !== event) {
|
|
6910
|
+
requestAnimationFrame(() => {
|
|
6911
|
+
const e = pending;
|
|
6912
|
+
pending = NIL;
|
|
6913
|
+
process(e);
|
|
6914
|
+
});
|
|
6915
|
+
}
|
|
6916
|
+
pending = event;
|
|
6917
|
+
}
|
|
6918
|
+
return debounce ? delay : process;
|
|
6919
|
+
}
|
|
6920
|
+
|
|
6921
|
+
// ../core/src/MosaicClient.js
|
|
6922
|
+
var MosaicClient = class {
|
|
6923
|
+
/**
|
|
6924
|
+
* Constructor.
|
|
6925
|
+
* @param {*} filterSelection An optional selection to interactively filter
|
|
6926
|
+
* this client's data. If provided, a coordinator will re-query and update
|
|
6927
|
+
* the client when the selection updates.
|
|
6928
|
+
*/
|
|
6929
|
+
constructor(filterSelection) {
|
|
6930
|
+
this._filterBy = filterSelection;
|
|
6931
|
+
this._requestUpdate = throttle(() => this.requestQuery(), true);
|
|
6932
|
+
this._coordinator = null;
|
|
6933
|
+
this._pending = Promise.resolve();
|
|
6934
|
+
}
|
|
6935
|
+
/**
|
|
6936
|
+
* Return this client's connected coordinator.
|
|
6937
|
+
*/
|
|
6938
|
+
get coordinator() {
|
|
6939
|
+
return this._coordinator;
|
|
6940
|
+
}
|
|
6941
|
+
/**
|
|
6942
|
+
* Set this client's connected coordinator.
|
|
6943
|
+
*/
|
|
6944
|
+
set coordinator(coordinator2) {
|
|
6945
|
+
this._coordinator = coordinator2;
|
|
6946
|
+
}
|
|
6947
|
+
/**
|
|
6948
|
+
* Return a Promise that resolves once the client has updated.
|
|
6949
|
+
*/
|
|
6950
|
+
get pending() {
|
|
6951
|
+
return this._pending;
|
|
6952
|
+
}
|
|
6953
|
+
/**
|
|
6954
|
+
* Return this client's filter selection.
|
|
6955
|
+
*/
|
|
6956
|
+
get filterBy() {
|
|
6957
|
+
return this._filterBy;
|
|
6958
|
+
}
|
|
6959
|
+
/**
|
|
6960
|
+
* Return a boolean indicating if the client query can be sped up with
|
|
6961
|
+
* materialized views of pre-aggregated data. Should return true if changes to
|
|
6962
|
+
* the filterBy selection does not change the groupby domain of the client
|
|
6963
|
+
* query.
|
|
6964
|
+
*/
|
|
6965
|
+
get filterStable() {
|
|
6966
|
+
return true;
|
|
6967
|
+
}
|
|
6968
|
+
/**
|
|
6969
|
+
* Return an array of fields queried by this client.
|
|
6970
|
+
* @returns {import('./types.js').FieldInfoRequest[] | null}
|
|
6971
|
+
* The fields to retrieve info for.
|
|
6972
|
+
*/
|
|
6973
|
+
fields() {
|
|
6974
|
+
return null;
|
|
6975
|
+
}
|
|
6976
|
+
/**
|
|
6977
|
+
* Called by the coordinator to set the field info for this client.
|
|
6978
|
+
* @param {import('./types.js').FieldInfo[]} info The field info result.
|
|
6979
|
+
* @returns {this}
|
|
6980
|
+
*/
|
|
6981
|
+
fieldInfo(info) {
|
|
6982
|
+
return this;
|
|
6983
|
+
}
|
|
6984
|
+
/**
|
|
6985
|
+
* Prepare the client before the query() method is called.
|
|
6986
|
+
*/
|
|
6987
|
+
async prepare() {
|
|
6988
|
+
}
|
|
6989
|
+
/**
|
|
6990
|
+
* Return a query specifying the data needed by this client.
|
|
6991
|
+
* @param {*} [filter] The filtering criteria to apply in the query.
|
|
6992
|
+
* @returns {*} The client query
|
|
6993
|
+
*/
|
|
6994
|
+
query(filter3) {
|
|
6995
|
+
return null;
|
|
6996
|
+
}
|
|
6997
|
+
/**
|
|
6998
|
+
* Called by the coordinator to inform the client that a query is pending.
|
|
6999
|
+
* @returns {this}
|
|
7000
|
+
*/
|
|
7001
|
+
queryPending() {
|
|
7002
|
+
return this;
|
|
7003
|
+
}
|
|
7004
|
+
/**
|
|
7005
|
+
* Called by the coordinator to return a query result.
|
|
7006
|
+
* @param {*} data The query result.
|
|
7007
|
+
* @returns {this}
|
|
7008
|
+
*/
|
|
7009
|
+
queryResult(data) {
|
|
7010
|
+
return this;
|
|
7011
|
+
}
|
|
7012
|
+
/**
|
|
7013
|
+
* Called by the coordinator to report a query execution error.
|
|
7014
|
+
* @param {*} error
|
|
7015
|
+
* @returns {this}
|
|
7016
|
+
*/
|
|
7017
|
+
queryError(error) {
|
|
7018
|
+
return this;
|
|
7019
|
+
}
|
|
7020
|
+
/**
|
|
7021
|
+
* Request the coordinator to execute a query for this client.
|
|
7022
|
+
* If an explicit query is not provided, the client query method will
|
|
7023
|
+
* be called, filtered by the current filterBy selection.
|
|
7024
|
+
* @returns {Promise}
|
|
7025
|
+
*/
|
|
7026
|
+
requestQuery(query) {
|
|
7027
|
+
const q = query || this.query(this.filterBy?.predicate(this));
|
|
7028
|
+
return this._coordinator.requestQuery(this, q);
|
|
7029
|
+
}
|
|
7030
|
+
/**
|
|
7031
|
+
* Request that the coordinator perform a throttled update of this client
|
|
7032
|
+
* using the default query. Unlike requestQuery, for which every call will
|
|
7033
|
+
* result in an executed query, multiple calls to requestUpdate may be
|
|
7034
|
+
* consolidated into a single update.
|
|
7035
|
+
*/
|
|
7036
|
+
requestUpdate() {
|
|
7037
|
+
this._requestUpdate();
|
|
7038
|
+
}
|
|
7039
|
+
/**
|
|
7040
|
+
* Reset this client, initiating new field info, call the prepare method, and query requests.
|
|
7041
|
+
* @returns {Promise}
|
|
7042
|
+
*/
|
|
7043
|
+
initialize() {
|
|
7044
|
+
return this._coordinator.initializeClient(this);
|
|
7045
|
+
}
|
|
7046
|
+
/**
|
|
7047
|
+
* Requests a client update.
|
|
7048
|
+
* For example to (re-)render an interface component.
|
|
7049
|
+
*
|
|
7050
|
+
* @returns {this | Promise<any>}
|
|
7051
|
+
*/
|
|
7052
|
+
update() {
|
|
7053
|
+
return this;
|
|
7054
|
+
}
|
|
7055
|
+
};
|
|
7056
|
+
|
|
7028
7057
|
// ../core/src/SelectionClause.js
|
|
7029
7058
|
function clausePoint(field3, value, {
|
|
7030
7059
|
source,
|
|
@@ -30992,6 +31021,9 @@ var DEFAULT_ATTRIBUTES = {
|
|
|
30992
31021
|
marginBottom: 30
|
|
30993
31022
|
};
|
|
30994
31023
|
var Plot = class {
|
|
31024
|
+
/**
|
|
31025
|
+
* @param {HTMLElement} [element]
|
|
31026
|
+
*/
|
|
30995
31027
|
constructor(element) {
|
|
30996
31028
|
this.attributes = { ...DEFAULT_ATTRIBUTES };
|
|
30997
31029
|
this.listeners = null;
|
|
@@ -30999,12 +31031,12 @@ var Plot = class {
|
|
|
30999
31031
|
this.legends = [];
|
|
31000
31032
|
this.marks = [];
|
|
31001
31033
|
this.markset = null;
|
|
31034
|
+
this.params = /* @__PURE__ */ new Map();
|
|
31035
|
+
this.synch = synchronizer();
|
|
31002
31036
|
this.element = element || document.createElement("div");
|
|
31003
31037
|
this.element.setAttribute("class", "plot");
|
|
31004
31038
|
this.element.style.display = "flex";
|
|
31005
|
-
this.element
|
|
31006
|
-
this.params = /* @__PURE__ */ new Map();
|
|
31007
|
-
this.synch = synchronizer();
|
|
31039
|
+
Object.assign(this.element, { value: this });
|
|
31008
31040
|
}
|
|
31009
31041
|
margins() {
|
|
31010
31042
|
return {
|
|
@@ -31148,7 +31180,9 @@ function isColor2(value) {
|
|
|
31148
31180
|
|
|
31149
31181
|
// src/marks/util/is-constant-option.js
|
|
31150
31182
|
var constantOptions = /* @__PURE__ */ new Set([
|
|
31183
|
+
"offset",
|
|
31151
31184
|
"order",
|
|
31185
|
+
"reverse",
|
|
31152
31186
|
"sort",
|
|
31153
31187
|
"label",
|
|
31154
31188
|
"anchor",
|
|
@@ -31220,7 +31254,7 @@ var isFieldObject = (channel, field3) => {
|
|
|
31220
31254
|
var fieldEntry = (channel, field3) => ({
|
|
31221
31255
|
channel,
|
|
31222
31256
|
field: field3,
|
|
31223
|
-
as: isColumnRef(field3) ? field3.column : channel
|
|
31257
|
+
as: isColumnRef(field3) && !isColumnParam(field3) ? field3.column : channel
|
|
31224
31258
|
});
|
|
31225
31259
|
var valueEntry = (channel, value) => ({ channel, value });
|
|
31226
31260
|
var isDataArray = (source) => Array.isArray(source);
|
|
@@ -31316,7 +31350,11 @@ var Mark2 = class extends MosaicClient {
|
|
|
31316
31350
|
reqs[channel]?.forEach((s2) => entry.add(s2));
|
|
31317
31351
|
}
|
|
31318
31352
|
const table = this.sourceTable();
|
|
31319
|
-
return Array.from(fields, ([c4, s2]) => ({
|
|
31353
|
+
return Array.from(fields, ([c4, s2]) => ({
|
|
31354
|
+
table,
|
|
31355
|
+
column: c4,
|
|
31356
|
+
stats: Array.from(s2)
|
|
31357
|
+
}));
|
|
31320
31358
|
}
|
|
31321
31359
|
fieldInfo(info) {
|
|
31322
31360
|
const lookup = Object.fromEntries(info.map((x3) => [x3.column, x3]));
|
|
@@ -31373,7 +31411,7 @@ function markQuery(channels, table, skip = []) {
|
|
|
31373
31411
|
const { channel, field: field3, as } = c4;
|
|
31374
31412
|
if (skip.includes(channel)) continue;
|
|
31375
31413
|
if (channel === "orderby") {
|
|
31376
|
-
q.orderby(c4.value);
|
|
31414
|
+
q.orderby(c4.value ?? field3);
|
|
31377
31415
|
} else if (field3) {
|
|
31378
31416
|
if (isAggregateExpression(field3)) {
|
|
31379
31417
|
aggr = true;
|
|
@@ -31505,7 +31543,7 @@ var ConnectedMark = class extends Mark2 {
|
|
|
31505
31543
|
const cols = q._select.map((c4) => c4.alias).filter((c4) => c4 !== as && c4 !== value);
|
|
31506
31544
|
return m4(q, expr, as, value, cols);
|
|
31507
31545
|
} else {
|
|
31508
|
-
return q.orderby(
|
|
31546
|
+
return q.orderby(as);
|
|
31509
31547
|
}
|
|
31510
31548
|
}
|
|
31511
31549
|
};
|
|
@@ -31514,13 +31552,33 @@ var ConnectedMark = class extends Mark2 {
|
|
|
31514
31552
|
function array3(size, proto = []) {
|
|
31515
31553
|
return new proto.constructor(size);
|
|
31516
31554
|
}
|
|
31517
|
-
function grid1d(size, index2, value) {
|
|
31518
|
-
const
|
|
31519
|
-
const
|
|
31520
|
-
|
|
31521
|
-
|
|
31555
|
+
function grid1d(size, index2, value, columns, groupby) {
|
|
31556
|
+
const numRows = index2.length;
|
|
31557
|
+
const result = {};
|
|
31558
|
+
const cells = [];
|
|
31559
|
+
if (groupby?.length) {
|
|
31560
|
+
const group3 = new Int32Array(numRows);
|
|
31561
|
+
const gvalues = groupby.map((name) => columns[name]);
|
|
31562
|
+
const cellMap = {};
|
|
31563
|
+
for (let row = 0; row < numRows; ++row) {
|
|
31564
|
+
const key = gvalues.map((group4) => group4[row]);
|
|
31565
|
+
group3[row] = cellMap[key] ??= cells.push(key) - 1;
|
|
31566
|
+
}
|
|
31567
|
+
for (let i = 0; i < groupby.length; ++i) {
|
|
31568
|
+
result[groupby[i]] = cells.map((cell2) => cell2[i]);
|
|
31569
|
+
}
|
|
31570
|
+
const G = result._grid = cells.map(() => array3(size, value));
|
|
31571
|
+
for (let row = 0; row < numRows; ++row) {
|
|
31572
|
+
G[group3[row]][index2[row]] = value[row];
|
|
31573
|
+
}
|
|
31574
|
+
} else {
|
|
31575
|
+
cells.push([]);
|
|
31576
|
+
const [G] = result._grid = [array3(size, value)];
|
|
31577
|
+
for (let row = 0; row < numRows; ++row) {
|
|
31578
|
+
G[index2[row]] = value[row];
|
|
31579
|
+
}
|
|
31522
31580
|
}
|
|
31523
|
-
return
|
|
31581
|
+
return { numRows: cells.length, columns: result };
|
|
31524
31582
|
}
|
|
31525
31583
|
function grid2d(w, h, index2, columns, aggregates, groupby, interpolate) {
|
|
31526
31584
|
const numRows = index2.length;
|
|
@@ -32378,9 +32436,16 @@ function stripXY(mark, filter3) {
|
|
|
32378
32436
|
}
|
|
32379
32437
|
|
|
32380
32438
|
// src/marks/Density1DMark.js
|
|
32439
|
+
var GROUPBY = { fill: 1, stroke: 1, z: 1 };
|
|
32381
32440
|
var Density1DMark = class extends Mark2 {
|
|
32382
32441
|
constructor(type2, source, options) {
|
|
32383
|
-
const {
|
|
32442
|
+
const {
|
|
32443
|
+
bins: bins2 = 1024,
|
|
32444
|
+
bandwidth = 20,
|
|
32445
|
+
normalize: normalize4 = false,
|
|
32446
|
+
stack: stack2 = false,
|
|
32447
|
+
...channels
|
|
32448
|
+
} = options;
|
|
32384
32449
|
const dim = type2.endsWith("X") ? "y" : "x";
|
|
32385
32450
|
super(type2, source, channels, dim === "x" ? xext : yext);
|
|
32386
32451
|
this.dim = dim;
|
|
@@ -32389,7 +32454,13 @@ var Density1DMark = class extends Mark2 {
|
|
|
32389
32454
|
});
|
|
32390
32455
|
this.bandwidth = handleParam(bandwidth, (value) => {
|
|
32391
32456
|
this.bandwidth = value;
|
|
32392
|
-
return this.
|
|
32457
|
+
return this.grids ? this.convolve().update() : null;
|
|
32458
|
+
});
|
|
32459
|
+
this.normalize = handleParam(normalize4, (value) => {
|
|
32460
|
+
return this.normalize = value, this.convolve().update();
|
|
32461
|
+
});
|
|
32462
|
+
this.stack = handleParam(stack2, (value) => {
|
|
32463
|
+
return this.stack = value, this.update();
|
|
32393
32464
|
});
|
|
32394
32465
|
}
|
|
32395
32466
|
get filterStable() {
|
|
@@ -32404,42 +32475,67 @@ var Density1DMark = class extends Mark2 {
|
|
|
32404
32475
|
const [x3, bx] = binExpr(this, dim, bins2, extent4);
|
|
32405
32476
|
const q = markQuery(channels, this.sourceTable(), [dim]).where(filter3.concat(isBetween(bx, extent4)));
|
|
32406
32477
|
const v2 = this.channelField("weight") ? "weight" : null;
|
|
32407
|
-
|
|
32478
|
+
const g = this.groupby = channels.flatMap((c4) => {
|
|
32479
|
+
return GROUPBY[c4.channel] && c4.field ? c4.as : [];
|
|
32480
|
+
});
|
|
32481
|
+
return binLinear1d(q, x3, v2, g);
|
|
32408
32482
|
}
|
|
32409
32483
|
queryResult(data) {
|
|
32410
|
-
const
|
|
32411
|
-
this.
|
|
32484
|
+
const c4 = toDataColumns(data).columns;
|
|
32485
|
+
this.grids = grid1d(this.bins, c4.index, c4.density, c4, this.groupby);
|
|
32412
32486
|
return this.convolve();
|
|
32413
32487
|
}
|
|
32414
32488
|
convolve() {
|
|
32415
|
-
const {
|
|
32416
|
-
|
|
32489
|
+
const {
|
|
32490
|
+
bins: bins2,
|
|
32491
|
+
bandwidth,
|
|
32492
|
+
normalize: normalize4,
|
|
32493
|
+
dim,
|
|
32494
|
+
grids,
|
|
32495
|
+
groupby,
|
|
32496
|
+
plot: plot2,
|
|
32497
|
+
extent: [lo, hi]
|
|
32498
|
+
} = this;
|
|
32499
|
+
const cols = grids.columns;
|
|
32500
|
+
const numGrids = grids.numRows;
|
|
32501
|
+
const b = this.channelField(dim).as;
|
|
32502
|
+
const v2 = dim === "x" ? "y" : "x";
|
|
32417
32503
|
const size = dim === "x" ? plot2.innerWidth() : plot2.innerHeight();
|
|
32504
|
+
const neg = cols._grid.some((grid) => grid.some((v3) => v3 < 0));
|
|
32418
32505
|
const config = dericheConfig(bandwidth * (bins2 - 1) / size, neg);
|
|
32419
|
-
const result = dericheConv1d(config, grid, bins2);
|
|
32420
|
-
const v2 = dim === "x" ? "y" : "x";
|
|
32421
|
-
const b = this.channelField(dim).as;
|
|
32422
32506
|
const b0 = +lo;
|
|
32423
32507
|
const delta = (hi - b0) / (bins2 - 1);
|
|
32424
|
-
const
|
|
32425
|
-
const _b = new Float64Array(
|
|
32426
|
-
const _v = new Float64Array(
|
|
32427
|
-
|
|
32428
|
-
|
|
32429
|
-
|
|
32430
|
-
|
|
32431
|
-
|
|
32508
|
+
const numRows = bins2 * numGrids;
|
|
32509
|
+
const _b = new Float64Array(numRows);
|
|
32510
|
+
const _v = new Float64Array(numRows);
|
|
32511
|
+
const _g = groupby.reduce((m, name) => (m[name] = Array(numRows), m), {});
|
|
32512
|
+
for (let k2 = 0, g = 0; g < numGrids; ++g) {
|
|
32513
|
+
groupby.forEach((name) => _g[name].fill(cols[name][g], k2, k2 + bins2));
|
|
32514
|
+
const grid = cols._grid[g];
|
|
32515
|
+
const result = dericheConv1d(config, grid, bins2);
|
|
32516
|
+
const scale3 = 1 / norm(grid, result, delta, normalize4);
|
|
32517
|
+
for (let i = 0; i < bins2; ++i, ++k2) {
|
|
32518
|
+
_b[k2] = b0 + i * delta;
|
|
32519
|
+
_v[k2] = result[i] * scale3;
|
|
32520
|
+
}
|
|
32521
|
+
}
|
|
32522
|
+
this.data = { numRows, columns: { [b]: _b, [v2]: _v, ..._g } };
|
|
32432
32523
|
return this;
|
|
32433
32524
|
}
|
|
32434
32525
|
plotSpecs() {
|
|
32435
|
-
const { type: type2, data: { numRows: length4, columns }, channels, dim } = this;
|
|
32436
|
-
const
|
|
32526
|
+
const { type: type2, data: { numRows: length4, columns }, channels, dim, stack: stack2 } = this;
|
|
32527
|
+
const _ = type2.startsWith("area") && !stack2 ? "2" : "";
|
|
32528
|
+
const options = dim === "x" ? { [`y${_}`]: columns.y } : { [`x${_}`]: columns.x };
|
|
32437
32529
|
for (const c4 of channels) {
|
|
32438
32530
|
options[c4.channel] = channelOption(c4, columns);
|
|
32439
32531
|
}
|
|
32440
32532
|
return [{ type: type2, data: { length: length4 }, options }];
|
|
32441
32533
|
}
|
|
32442
32534
|
};
|
|
32535
|
+
function norm(grid, smoothed, delta, type2) {
|
|
32536
|
+
const value = type2 === true || type2 === "sum" ? sum2(grid) : type2 === "max" ? max2(smoothed) : delta;
|
|
32537
|
+
return value || 1;
|
|
32538
|
+
}
|
|
32443
32539
|
|
|
32444
32540
|
// src/marks/Density2DMark.js
|
|
32445
32541
|
var Density2DMark = class extends Grid2DMark {
|
|
@@ -33607,12 +33703,13 @@ var Nearest = class {
|
|
|
33607
33703
|
selection2.update(that.clause(void 0));
|
|
33608
33704
|
});
|
|
33609
33705
|
svg.addEventListener("pointerenter", (evt) => {
|
|
33610
|
-
if (!evt.buttons)
|
|
33611
|
-
const v2 = this.channels.map(() => 0);
|
|
33612
|
-
selection2.activate(this.clause(v2));
|
|
33613
|
-
}
|
|
33706
|
+
if (!evt.buttons) this.activate();
|
|
33614
33707
|
});
|
|
33615
33708
|
}
|
|
33709
|
+
activate() {
|
|
33710
|
+
const v2 = this.channels.map(() => 0);
|
|
33711
|
+
this.selection.activate(this.clause(v2));
|
|
33712
|
+
}
|
|
33616
33713
|
};
|
|
33617
33714
|
function calculateXY(svg, mark) {
|
|
33618
33715
|
const { data: { columns } } = mark;
|
|
@@ -33708,7 +33805,7 @@ var PanZoom = class {
|
|
|
33708
33805
|
this.svg = svg;
|
|
33709
33806
|
if (this.initialized) return;
|
|
33710
33807
|
else this.initialized = true;
|
|
33711
|
-
const { panx, pany, mark: { plot: { element } }
|
|
33808
|
+
const { panx, pany, mark: { plot: { element } } } = this;
|
|
33712
33809
|
this.xscale = svg.scale("x");
|
|
33713
33810
|
this.yscale = svg.scale("y");
|
|
33714
33811
|
const rx = this.xscale.range.slice().sort(asc2);
|
|
@@ -33725,19 +33822,21 @@ var PanZoom = class {
|
|
|
33725
33822
|
element.addEventListener("pointerenter", (evt) => {
|
|
33726
33823
|
if (enter) return;
|
|
33727
33824
|
else enter = true;
|
|
33728
|
-
if (evt.buttons)
|
|
33729
|
-
if (panx) {
|
|
33730
|
-
const { xscale, xfield } = this;
|
|
33731
|
-
xsel.activate(this.clause(xscale.domain, xfield, xscale));
|
|
33732
|
-
}
|
|
33733
|
-
if (pany) {
|
|
33734
|
-
const { yscale, yfield } = this;
|
|
33735
|
-
ysel.activate(this.clause(yscale.domain, yfield, yscale));
|
|
33736
|
-
}
|
|
33825
|
+
if (!evt.buttons) this.activate();
|
|
33737
33826
|
});
|
|
33738
33827
|
element.addEventListener("pointerleave", () => enter = false);
|
|
33739
33828
|
}
|
|
33740
33829
|
}
|
|
33830
|
+
activate() {
|
|
33831
|
+
if (this.panx) {
|
|
33832
|
+
const { xscale, xfield } = this;
|
|
33833
|
+
this.xsel.activate(this.clause(xscale.domain, xfield, xscale));
|
|
33834
|
+
}
|
|
33835
|
+
if (this.pany) {
|
|
33836
|
+
const { yscale, yfield } = this;
|
|
33837
|
+
this.ysel.activate(this.clause(yscale.domain, yfield, yscale));
|
|
33838
|
+
}
|
|
33839
|
+
}
|
|
33741
33840
|
};
|
|
33742
33841
|
function extent3(ext, defaultTrue, defaultFalse) {
|
|
33743
33842
|
return ext ? Array.isArray(ext) ? ext : defaultTrue : defaultFalse;
|
|
@@ -34122,8 +34221,8 @@ var Toggle = class {
|
|
|
34122
34221
|
channels,
|
|
34123
34222
|
peers = true
|
|
34124
34223
|
}) {
|
|
34125
|
-
this.value = null;
|
|
34126
34224
|
this.mark = mark;
|
|
34225
|
+
this.value = null;
|
|
34127
34226
|
this.selection = selection2;
|
|
34128
34227
|
this.peers = peers;
|
|
34129
34228
|
const fields = this.fields = [];
|
|
@@ -34175,10 +34274,12 @@ var Toggle = class {
|
|
|
34175
34274
|
}
|
|
34176
34275
|
});
|
|
34177
34276
|
svg.addEventListener("pointerenter", (evt) => {
|
|
34178
|
-
if (evt.buttons)
|
|
34179
|
-
this.selection.activate(this.clause([this.fields.map(() => 0)]));
|
|
34277
|
+
if (!evt.buttons) this.activate();
|
|
34180
34278
|
});
|
|
34181
34279
|
}
|
|
34280
|
+
activate() {
|
|
34281
|
+
this.selection.activate(this.clause([this.fields.map(() => 0)]));
|
|
34282
|
+
}
|
|
34182
34283
|
};
|
|
34183
34284
|
function isTargetElement(groups2, node) {
|
|
34184
34285
|
return groups2.some((g) => g.contains(node));
|