@uwdata/mosaic-plot 0.12.2 → 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 +225 -195
- 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/Mark.js +1 -1
- package/src/plot.js +14 -3
- package/vitest.config.ts +3 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uwdata/mosaic-plot",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "A Mosaic-powered plotting framework based on Observable Plot.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"data",
|
|
@@ -12,27 +12,24 @@
|
|
|
12
12
|
"license": "BSD-3-Clause",
|
|
13
13
|
"author": "Jeffrey Heer (https://idl.uw.edu)",
|
|
14
14
|
"type": "module",
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
"unpkg": "dist/mosaic-plot.min.js",
|
|
15
|
+
"exports": {
|
|
16
|
+
"default": "./src/index.js"
|
|
17
|
+
},
|
|
19
18
|
"repository": {
|
|
20
19
|
"type": "git",
|
|
21
20
|
"url": "https://github.com/uwdata/mosaic.git"
|
|
22
21
|
},
|
|
23
22
|
"scripts": {
|
|
24
|
-
"prebuild": "rimraf dist && mkdir dist",
|
|
25
|
-
"build": "node ../../esbuild.js mosaic-plot",
|
|
26
23
|
"lint": "eslint src test",
|
|
27
24
|
"test": "vitest run",
|
|
28
|
-
"prepublishOnly": "npm run test && npm run lint
|
|
25
|
+
"prepublishOnly": "npm run test && npm run lint"
|
|
29
26
|
},
|
|
30
27
|
"dependencies": {
|
|
31
|
-
"@observablehq/plot": "^0.6.
|
|
32
|
-
"@uwdata/mosaic-core": "^0.
|
|
33
|
-
"@uwdata/mosaic-sql": "^0.
|
|
28
|
+
"@observablehq/plot": "^0.6.17",
|
|
29
|
+
"@uwdata/mosaic-core": "^0.13.0",
|
|
30
|
+
"@uwdata/mosaic-sql": "^0.13.0",
|
|
34
31
|
"d3": "^7.9.0",
|
|
35
32
|
"isoformat": "^0.2.1"
|
|
36
33
|
},
|
|
37
|
-
"gitHead": "
|
|
34
|
+
"gitHead": "b5a0e03e200c0f04c46562a288f084ffc9f6ad55"
|
|
38
35
|
}
|
|
@@ -6,6 +6,10 @@ import { getField } from './util/get-field.js';
|
|
|
6
6
|
import { invert } from './util/invert.js';
|
|
7
7
|
import { sanitizeStyles } from './util/sanitize-styles.js';
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* @import {Activatable} from '@uwdata/mosaic-core'
|
|
11
|
+
* @implements {Activatable}
|
|
12
|
+
*/
|
|
9
13
|
export class Interval1D {
|
|
10
14
|
constructor(mark, {
|
|
11
15
|
channel,
|
|
@@ -6,6 +6,10 @@ import { getField } from './util/get-field.js';
|
|
|
6
6
|
import { invert } from './util/invert.js';
|
|
7
7
|
import { sanitizeStyles } from './util/sanitize-styles.js';
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* @import {Activatable} from '@uwdata/mosaic-core'
|
|
11
|
+
* @implements {Activatable}
|
|
12
|
+
*/
|
|
9
13
|
export class Interval2D {
|
|
10
14
|
constructor(mark, {
|
|
11
15
|
selection,
|
|
@@ -2,6 +2,10 @@ import { clausePoint, clausePoints, isSelection } from '@uwdata/mosaic-core';
|
|
|
2
2
|
import { select, pointer, min } from 'd3';
|
|
3
3
|
import { getField } from './util/get-field.js';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* @import {Activatable} from '@uwdata/mosaic-core'
|
|
7
|
+
* @implements {Activatable}
|
|
8
|
+
*/
|
|
5
9
|
export class Nearest {
|
|
6
10
|
constructor(mark, {
|
|
7
11
|
selection,
|
|
@@ -71,12 +75,14 @@ export class Nearest {
|
|
|
71
75
|
|
|
72
76
|
// trigger activation updates
|
|
73
77
|
svg.addEventListener('pointerenter', evt => {
|
|
74
|
-
if (!evt.buttons)
|
|
75
|
-
const v = this.channels.map(() => 0);
|
|
76
|
-
selection.activate(this.clause(v));
|
|
77
|
-
}
|
|
78
|
+
if (!evt.buttons) this.activate();
|
|
78
79
|
});
|
|
79
80
|
}
|
|
81
|
+
|
|
82
|
+
activate() {
|
|
83
|
+
const v = this.channels.map(() => 0);
|
|
84
|
+
this.selection.activate(this.clause(v));
|
|
85
|
+
}
|
|
80
86
|
}
|
|
81
87
|
|
|
82
88
|
/**
|
|
@@ -4,6 +4,10 @@ import { getField } from './util/get-field.js';
|
|
|
4
4
|
|
|
5
5
|
const asc = (a, b) => a - b;
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* @import {Activatable} from '@uwdata/mosaic-core'
|
|
9
|
+
* @implements {Activatable}
|
|
10
|
+
*/
|
|
7
11
|
export class PanZoom {
|
|
8
12
|
constructor(mark, {
|
|
9
13
|
x = new Selection(),
|
|
@@ -59,7 +63,7 @@ export class PanZoom {
|
|
|
59
63
|
this.svg = svg;
|
|
60
64
|
if (this.initialized) return; else this.initialized = true;
|
|
61
65
|
|
|
62
|
-
const { panx, pany, mark: { plot: { element } }
|
|
66
|
+
const { panx, pany, mark: { plot: { element } } } = this;
|
|
63
67
|
|
|
64
68
|
this.xscale = svg.scale('x');
|
|
65
69
|
this.yscale = svg.scale('y');
|
|
@@ -85,19 +89,22 @@ export class PanZoom {
|
|
|
85
89
|
let enter = false;
|
|
86
90
|
element.addEventListener('pointerenter', evt => {
|
|
87
91
|
if (enter) return; else enter = true;
|
|
88
|
-
if (evt.buttons)
|
|
89
|
-
if (panx) {
|
|
90
|
-
const { xscale, xfield } = this;
|
|
91
|
-
xsel.activate(this.clause(xscale.domain, xfield, xscale));
|
|
92
|
-
}
|
|
93
|
-
if (pany) {
|
|
94
|
-
const { yscale, yfield } = this;
|
|
95
|
-
ysel.activate(this.clause(yscale.domain, yfield, yscale));
|
|
96
|
-
}
|
|
92
|
+
if (!evt.buttons) this.activate(); // don't activate if mouse down
|
|
97
93
|
});
|
|
98
94
|
element.addEventListener('pointerleave', () => enter = false);
|
|
99
95
|
}
|
|
100
96
|
}
|
|
97
|
+
|
|
98
|
+
activate() {
|
|
99
|
+
if (this.panx) {
|
|
100
|
+
const { xscale, xfield } = this;
|
|
101
|
+
this.xsel.activate(this.clause(xscale.domain, xfield, xscale));
|
|
102
|
+
}
|
|
103
|
+
if (this.pany) {
|
|
104
|
+
const { yscale, yfield } = this;
|
|
105
|
+
this.ysel.activate(this.clause(yscale.domain, yfield, yscale));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
101
108
|
}
|
|
102
109
|
|
|
103
110
|
function extent(ext, defaultTrue, defaultFalse) {
|
|
@@ -8,6 +8,10 @@ import { sanitizeStyles } from './util/sanitize-styles.js';
|
|
|
8
8
|
import { neqSome } from './util/neq.js';
|
|
9
9
|
import { getDatum } from './util/get-datum.js';
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* @import {Activatable} from '@uwdata/mosaic-core'
|
|
13
|
+
* @implements {Activatable}
|
|
14
|
+
*/
|
|
11
15
|
export class Region {
|
|
12
16
|
constructor(mark, {
|
|
13
17
|
channels,
|
|
@@ -2,6 +2,10 @@ import { clausePoints } from '@uwdata/mosaic-core';
|
|
|
2
2
|
import { getDatum } from './util/get-datum.js';
|
|
3
3
|
import { neq, neqSome } from './util/neq.js';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* @import {Activatable} from '@uwdata/mosaic-core'
|
|
7
|
+
* @implements {Activatable}
|
|
8
|
+
*/
|
|
5
9
|
export class Toggle {
|
|
6
10
|
/**
|
|
7
11
|
* @param {*} mark The mark to interact with.
|
|
@@ -12,8 +16,8 @@ export class Toggle {
|
|
|
12
16
|
channels,
|
|
13
17
|
peers = true
|
|
14
18
|
}) {
|
|
15
|
-
this.value = null;
|
|
16
19
|
this.mark = mark;
|
|
20
|
+
this.value = null;
|
|
17
21
|
this.selection = selection;
|
|
18
22
|
this.peers = peers;
|
|
19
23
|
const fields = this.fields = [];
|
|
@@ -75,10 +79,13 @@ export class Toggle {
|
|
|
75
79
|
});
|
|
76
80
|
|
|
77
81
|
svg.addEventListener('pointerenter', evt => {
|
|
78
|
-
if (evt.buttons)
|
|
79
|
-
this.selection.activate(this.clause([this.fields.map(() => 0)]));
|
|
82
|
+
if (!evt.buttons) this.activate();
|
|
80
83
|
});
|
|
81
84
|
}
|
|
85
|
+
|
|
86
|
+
activate() {
|
|
87
|
+
this.selection.activate(this.clause([this.fields.map(() => 0)]));
|
|
88
|
+
}
|
|
82
89
|
}
|
|
83
90
|
|
|
84
91
|
function isTargetElement(groups, node) {
|
package/src/marks/Mark.js
CHANGED
|
@@ -230,7 +230,7 @@ export function markQuery(channels, table, skip = []) {
|
|
|
230
230
|
if (skip.includes(channel)) continue;
|
|
231
231
|
|
|
232
232
|
if (channel === 'orderby') {
|
|
233
|
-
q.orderby(c.value);
|
|
233
|
+
q.orderby(c.value ?? field);
|
|
234
234
|
} else if (field) {
|
|
235
235
|
if (isAggregateExpression(field)) {
|
|
236
236
|
aggr = true;
|
package/src/plot.js
CHANGED
|
@@ -10,19 +10,30 @@ const DEFAULT_ATTRIBUTES = {
|
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
export class Plot {
|
|
13
|
+
/**
|
|
14
|
+
* @param {HTMLElement} [element]
|
|
15
|
+
*/
|
|
13
16
|
constructor(element) {
|
|
17
|
+
/** @type {Record<string, any>} */
|
|
14
18
|
this.attributes = { ...DEFAULT_ATTRIBUTES };
|
|
15
19
|
this.listeners = null;
|
|
16
20
|
this.interactors = [];
|
|
21
|
+
/** @type {{ legend: import('./legend.js').Legend, include: boolean }[]} */
|
|
17
22
|
this.legends = [];
|
|
23
|
+
/** @type {import('./marks/Mark.js').Mark[]} */
|
|
18
24
|
this.marks = [];
|
|
25
|
+
/** @type {Set<import('./marks/Mark.js').Mark> | null} */
|
|
19
26
|
this.markset = null;
|
|
27
|
+
/** @type {Map<import('@uwdata/mosaic-core').Param, import('./marks/Mark.js').Mark[]>} */
|
|
28
|
+
this.params = new Map;
|
|
29
|
+
/** @type {ReturnType<synchronizer>} */
|
|
30
|
+
this.synch = synchronizer();
|
|
31
|
+
|
|
32
|
+
/** @type {HTMLElement} */
|
|
20
33
|
this.element = element || document.createElement('div');
|
|
21
34
|
this.element.setAttribute('class', 'plot');
|
|
22
35
|
this.element.style.display = 'flex';
|
|
23
|
-
this.element
|
|
24
|
-
this.params = new Map;
|
|
25
|
-
this.synch = synchronizer();
|
|
36
|
+
Object.assign(this.element, { value: this });
|
|
26
37
|
}
|
|
27
38
|
|
|
28
39
|
margins() {
|
package/vitest.config.ts
ADDED