@uwdata/vgplot 0.4.0 → 0.6.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.
Files changed (65) hide show
  1. package/README.md +4 -2
  2. package/dist/vgplot.js +6677 -6503
  3. package/dist/vgplot.min.js +12 -33
  4. package/package.json +8 -10
  5. package/src/api.js +347 -0
  6. package/src/connect.js +14 -0
  7. package/src/context.js +20 -0
  8. package/src/index.js +14 -303
  9. package/src/inputs.js +24 -0
  10. package/src/{directives → plot}/attributes.js +32 -5
  11. package/src/{directives → plot}/interactors.js +8 -6
  12. package/src/{directives → plot}/legends.js +14 -6
  13. package/src/{directives → plot}/marks.js +18 -13
  14. package/src/plot/named-plots.js +49 -0
  15. package/src/plot/plot.js +9 -0
  16. package/src/directives/plot.js +0 -39
  17. package/src/interactors/Highlight.js +0 -101
  18. package/src/interactors/Interval1D.js +0 -90
  19. package/src/interactors/Interval2D.js +0 -102
  20. package/src/interactors/Nearest.js +0 -66
  21. package/src/interactors/PanZoom.js +0 -121
  22. package/src/interactors/Toggle.js +0 -111
  23. package/src/interactors/util/brush.js +0 -45
  24. package/src/interactors/util/close-to.js +0 -9
  25. package/src/interactors/util/get-field.js +0 -4
  26. package/src/interactors/util/invert.js +0 -3
  27. package/src/interactors/util/patchScreenCTM.js +0 -13
  28. package/src/interactors/util/sanitize-styles.js +0 -9
  29. package/src/interactors/util/to-kebab-case.js +0 -9
  30. package/src/layout/index.js +0 -2
  31. package/src/legend.js +0 -64
  32. package/src/marks/ConnectedMark.js +0 -63
  33. package/src/marks/ContourMark.js +0 -89
  34. package/src/marks/DenseLineMark.js +0 -146
  35. package/src/marks/Density1DMark.js +0 -104
  36. package/src/marks/Density2DMark.js +0 -69
  37. package/src/marks/Grid2DMark.js +0 -191
  38. package/src/marks/HexbinMark.js +0 -88
  39. package/src/marks/Mark.js +0 -195
  40. package/src/marks/RasterMark.js +0 -122
  41. package/src/marks/RasterTileMark.js +0 -332
  42. package/src/marks/RegressionMark.js +0 -117
  43. package/src/marks/util/bin-field.js +0 -17
  44. package/src/marks/util/density.js +0 -226
  45. package/src/marks/util/extent.js +0 -56
  46. package/src/marks/util/grid.js +0 -57
  47. package/src/marks/util/handle-param.js +0 -14
  48. package/src/marks/util/is-arrow-table.js +0 -3
  49. package/src/marks/util/is-color.js +0 -18
  50. package/src/marks/util/is-constant-option.js +0 -40
  51. package/src/marks/util/is-symbol.js +0 -20
  52. package/src/marks/util/raster.js +0 -44
  53. package/src/marks/util/stats.js +0 -133
  54. package/src/marks/util/to-data-array.js +0 -58
  55. package/src/plot-attributes.js +0 -211
  56. package/src/plot-renderer.js +0 -161
  57. package/src/plot.js +0 -136
  58. package/src/spec/parse-data.js +0 -69
  59. package/src/spec/parse-spec.js +0 -422
  60. package/src/spec/to-module.js +0 -465
  61. package/src/spec/util.js +0 -43
  62. package/src/symbols.js +0 -3
  63. package/src/transforms/bin.js +0 -81
  64. package/src/transforms/index.js +0 -3
  65. /package/src/{directives → plot}/data.js +0 -0
@@ -1,101 +0,0 @@
1
- import { coordinator, throttle } from '@uwdata/mosaic-core';
2
- import { and } from '@uwdata/mosaic-sql';
3
- import { sanitizeStyles } from './util/sanitize-styles.js';
4
-
5
- function configureMark(mark) {
6
- const { channels } = mark;
7
- const dims = new Set;
8
- let ordered = false;
9
- let aggregate = false;
10
-
11
- for (const c of channels) {
12
- const { channel, field, as } = c;
13
- if (channel === 'orderby') {
14
- ordered = true;
15
- } else if (field) {
16
- if (field.aggregate) {
17
- aggregate = true;
18
- } else {
19
- if (dims.has(as)) continue;
20
- dims.add(as);
21
- }
22
- }
23
- }
24
-
25
- // if orderby is defined, we're ok: nothing to do
26
- // or, if there is no groupby aggregation, we're ok: nothing to do
27
- // grouping may result in optimizations that change result order
28
- // so we orderby the grouping dimensions to ensure stable indices
29
- if (!ordered && aggregate && dims.size) {
30
- mark.channels.push(({ channel: 'orderby', value: Array.from(dims) }));
31
- }
32
-
33
- return mark;
34
- }
35
-
36
- export class Highlight {
37
- constructor(mark, {
38
- selection,
39
- channels = {}
40
- }) {
41
- this.mark = configureMark(mark);
42
- this.selection = selection;
43
- const c = Object.entries(sanitizeStyles(channels));
44
- this.channels = c.length ? c : [['opacity', 0.2]];
45
- this.selection.addEventListener('value', throttle(() => this.update()));
46
- }
47
-
48
- init(svg) {
49
- this.svg = svg;
50
- const values = this.values = [];
51
- const index = this.mark.index;
52
- const nodes = this.nodes = svg.querySelectorAll(`[data-index="${index}"] > *`);
53
-
54
- const { channels } = this;
55
- for (let i = 0; i < nodes.length; ++i) {
56
- const node = nodes[i];
57
- values.push(channels.map(c => node.getAttribute(c[0])));
58
- }
59
-
60
- return this.update();
61
- }
62
-
63
- async update() {
64
- const { svg, nodes, channels, values, mark, selection } = this;
65
- if (!svg) return;
66
-
67
- const test = await predicateFunction(mark, selection);
68
-
69
- for (let i = 0; i < nodes.length; ++i) {
70
- const node = nodes[i];
71
- const base = values[i];
72
- const t = test(node.__data__);
73
- // TODO? handle inherited values / remove attributes
74
- for (let j = 0; j < channels.length; ++j) {
75
- const [attr, value] = channels[j];
76
- node.setAttribute(attr, t ? base[j] : value);
77
- }
78
- }
79
- }
80
- }
81
-
82
- async function predicateFunction(mark, selection) {
83
- const pred = selection?.predicate(mark);
84
-
85
- if (!pred || pred.length === 0) {
86
- return () => true;
87
- }
88
-
89
- // set flag so we do not skip cross-filtered sources
90
- const filter = mark.filterBy?.predicate(mark, true);
91
-
92
- const s = { __: and(pred) };
93
- const q = mark.query(filter);
94
- const p = q.groupby().length ? q.select(s) : q.$select(s);
95
-
96
- const data = await coordinator().query(p);
97
- const v = data.getChild?.('__');
98
- return !(data.numRows || data.length) ? (() => false)
99
- : v ? (i => v.get(i))
100
- : (i => data[i].__);
101
- }
@@ -1,90 +0,0 @@
1
- import { select, min, max } from 'd3';
2
- import { isBetween } from '@uwdata/mosaic-sql';
3
- import { brushX, brushY } from './util/brush.js';
4
- import { closeTo } from './util/close-to.js';
5
- import { getField } from './util/get-field.js';
6
- import { invert } from './util/invert.js';
7
- import { patchScreenCTM } from './util/patchScreenCTM.js';
8
- import { sanitizeStyles } from './util/sanitize-styles.js';
9
-
10
- export class Interval1D {
11
- constructor(mark, {
12
- channel,
13
- selection,
14
- field,
15
- pixelSize = 1,
16
- peers = true,
17
- brush: style
18
- }) {
19
- this.mark = mark;
20
- this.channel = channel;
21
- this.pixelSize = pixelSize || 1;
22
- this.selection = selection;
23
- this.peers = peers;
24
- this.field = field || getField(mark, [channel, channel+'1', channel+'2']);
25
- this.style = style && sanitizeStyles(style);
26
- this.brush = channel === 'y' ? brushY() : brushX();
27
- this.brush.on('brush end', ({ selection }) => this.publish(selection));
28
- }
29
-
30
- reset() {
31
- this.value = undefined;
32
- if (this.g) this.brush.reset(this.g);
33
- }
34
-
35
- activate() {
36
- this.selection.activate(this.clause(this.value || [0, 1]));
37
- }
38
-
39
- publish(extent) {
40
- let range = undefined;
41
- if (extent) {
42
- range = extent
43
- .map(v => invert(v, this.scale, this.pixelSize))
44
- .sort((a, b) => a - b);
45
- }
46
- if (!closeTo(range, this.value)) {
47
- this.value = range;
48
- this.g.call(this.brush.moveSilent, extent);
49
- this.selection.update(this.clause(range));
50
- }
51
- }
52
-
53
- clause(value) {
54
- const { mark, pixelSize, field, scale } = this;
55
- return {
56
- source: this,
57
- schema: { type: 'interval', pixelSize, scales: [scale] },
58
- clients: this.peers ? mark.plot.markSet : new Set().add(mark),
59
- value,
60
- predicate: value ? isBetween(field, value) : null
61
- };
62
- }
63
-
64
- init(svg) {
65
- const { brush, channel, style } = this;
66
- this.scale = svg.scale(channel);
67
-
68
- const rx = svg.scale('x').range;
69
- const ry = svg.scale('y').range;
70
- brush.extent([[min(rx), min(ry)], [max(rx), max(ry)]]);
71
-
72
- const facets = select(svg).selectAll('g[aria-label="facet"]');
73
- const root = facets.size() ? facets : select(svg);
74
- this.g = root
75
- .append('g')
76
- .attr('class', `interval-${channel}`)
77
- .each(patchScreenCTM)
78
- .call(brush)
79
- .call(brush.moveSilent, this.value?.map(this.scale.apply));
80
-
81
- if (style) {
82
- const brushes = this.g.selectAll('rect.selection');
83
- for (const name in style) {
84
- brushes.attr(name, style[name]);
85
- }
86
- }
87
-
88
- svg.addEventListener('pointerenter', () => this.activate());
89
- }
90
- }
@@ -1,102 +0,0 @@
1
- import { select, min, max } from 'd3';
2
- import { and, isBetween } from '@uwdata/mosaic-sql';
3
- import { brush } from './util/brush.js';
4
- import { closeTo } from './util/close-to.js';
5
- import { getField } from './util/get-field.js';
6
- import { invert } from './util/invert.js';
7
- import { patchScreenCTM } from './util/patchScreenCTM.js';
8
- import { sanitizeStyles } from './util/sanitize-styles.js';
9
-
10
- const asc = (a, b) => a - b;
11
-
12
- export class Interval2D {
13
- constructor(mark, {
14
- selection,
15
- xfield,
16
- yfield,
17
- pixelSize = 1,
18
- peers = true,
19
- brush: style
20
- }) {
21
- this.mark = mark;
22
- this.pixelSize = pixelSize || 1;
23
- this.selection = selection;
24
- this.peers = peers;
25
- this.xfield = xfield || getField(mark, ['x', 'x1', 'x2']);
26
- this.yfield = yfield || getField(mark, ['y', 'y1', 'y2']);
27
- this.style = style && sanitizeStyles(style);
28
- this.brush = brush();
29
- this.brush.on('brush end', ({ selection }) => this.publish(selection));
30
- }
31
-
32
- reset() {
33
- this.value = undefined;
34
- if (this.g) this.brush.reset(this.g);
35
- }
36
-
37
- activate() {
38
- this.selection.activate(this.clause(this.value || [[0, 1], [0, 1]]));
39
- }
40
-
41
- publish(extent) {
42
- const { value, pixelSize, xscale, yscale } = this;
43
- let xr = undefined;
44
- let yr = undefined;
45
- if (extent) {
46
- const [a, b] = extent;
47
- xr = [a[0], b[0]].map(v => invert(v, xscale, pixelSize)).sort(asc);
48
- yr = [a[1], b[1]].map(v => invert(v, yscale, pixelSize)).sort(asc);
49
- }
50
-
51
- if (!closeTo(xr, value?.[0]) || !closeTo(yr, value?.[1])) {
52
- this.value = extent ? [xr, yr] : undefined;
53
- this.g.call(this.brush.moveSilent, extent);
54
- this.selection.update(this.clause(this.value));
55
- }
56
- }
57
-
58
- clause(value) {
59
- const { mark, pixelSize, xfield, yfield, xscale, yscale } = this;
60
- return {
61
- source: this,
62
- schema: { type: 'interval', pixelSize, scales: [xscale, yscale] },
63
- clients: this.peers ? mark.plot.markSet : new Set().add(mark),
64
- value,
65
- predicate: value
66
- ? and(isBetween(xfield, value[0]), isBetween(yfield, value[1]))
67
- : null
68
- };
69
- }
70
-
71
- init(svg) {
72
- const { brush, style } = this;
73
- const xscale = this.xscale = svg.scale('x');
74
- const yscale = this.yscale = svg.scale('y');
75
- const rx = xscale.range;
76
- const ry = yscale.range;
77
- brush.extent([[min(rx), min(ry)], [max(rx), max(ry)]]);
78
-
79
- const facets = select(svg).selectAll('g[aria-label="facet"]');
80
- const root = facets.size() ? facets : select(svg);
81
- this.g = root
82
- .append('g')
83
- .attr('class', `interval-xy`)
84
- .each(patchScreenCTM)
85
- .call(brush);
86
-
87
- if (style) {
88
- const brushes = this.g.selectAll('rect.selection');
89
- for (const name in style) {
90
- brushes.attr(name, style[name]);
91
- }
92
- }
93
-
94
- if (this.value) {
95
- const [x1, x2] = this.value[0].map(xscale.apply).sort(asc);
96
- const [y1, y2] = this.value[1].map(yscale.apply).sort(asc);
97
- this.g.call(brush.moveSilent, [[x1, y1], [x2, y2]]);
98
- }
99
-
100
- svg.addEventListener('pointerenter', () => this.activate());
101
- }
102
- }
@@ -1,66 +0,0 @@
1
- import { isSelection } from '@uwdata/mosaic-core';
2
- import { eq, literal } from '@uwdata/mosaic-sql';
3
- import { select, pointer } from 'd3';
4
- import { getField } from './util/get-field.js';
5
-
6
- export class Nearest {
7
- constructor(mark, {
8
- selection,
9
- channel,
10
- field
11
- }) {
12
- this.mark = mark;
13
- this.selection = selection;
14
- this.clients = new Set().add(mark);
15
- this.channel = channel;
16
- this.field = field || getField(mark, [channel]);
17
- }
18
-
19
- clause(value) {
20
- const { clients, field } = this;
21
- const predicate = value ? eq(field, literal(value)) : null;
22
- return {
23
- source: this,
24
- schema: { type: 'point' },
25
- clients,
26
- value,
27
- predicate
28
- };
29
- }
30
-
31
- init(svg) {
32
- const that = this;
33
- const { mark, channel, selection } = this;
34
- const { data } = mark;
35
- const key = mark.channelField(channel).as;
36
-
37
- const facets = select(svg).selectAll('g[aria-label="facet"]');
38
- const root = facets.size() ? facets : select(svg);
39
- const scale = svg.scale(channel);
40
- const param = !isSelection(selection);
41
-
42
- root.on('pointerdown pointermove', function(evt) {
43
- const [x, y] = pointer(evt, this);
44
- const z = findNearest(data, key, scale.invert(channel === 'x' ? x : y));
45
- selection.update(param ? z : that.clause(z));
46
- });
47
-
48
- if (param) return;
49
- svg.addEventListener('pointerenter', () => {
50
- this.selection.activate(this.clause(0));
51
- });
52
- }
53
- }
54
-
55
- function findNearest(data, key, value) {
56
- let dist = Infinity;
57
- let v;
58
- data.forEach(d => {
59
- const delta = Math.abs(d[key] - value);
60
- if (delta < dist) {
61
- dist = delta;
62
- v = d[key];
63
- }
64
- });
65
- return v;
66
- }
@@ -1,121 +0,0 @@
1
- import { select, zoom, ZoomTransform } from 'd3';
2
- import { Selection } from '@uwdata/mosaic-core';
3
- import { isBetween } from '@uwdata/mosaic-sql';
4
- import { getField } from './util/get-field.js';
5
-
6
- const asc = (a, b) => a - b;
7
-
8
- export class PanZoom {
9
- constructor(mark, {
10
- x = new Selection(),
11
- y = new Selection(),
12
- xfield,
13
- yfield,
14
- zoom = true,
15
- panx = true,
16
- pany = true
17
- }) {
18
- this.mark = mark;
19
- this.xsel = x;
20
- this.ysel = y;
21
- this.xfield = xfield || getField(mark, ['x', 'x1', 'x2']);
22
- this.yfield = yfield || getField(mark, ['y', 'y1', 'y2']);
23
- this.zoom = extent(zoom, [0, Infinity], [1, 1]);
24
- this.panx = this.xsel && panx;
25
- this.pany = this.ysel && pany;
26
-
27
- const { plot } = mark;
28
- if (panx) {
29
- this.xsel.addEventListener('value', value => {
30
- if (plot.setAttribute('xDomain', value)) plot.update();
31
- });
32
- }
33
- if (pany) {
34
- this.ysel.addEventListener('value', value => {
35
- if (plot.setAttribute('yDomain', value)) plot.update();
36
- });
37
- }
38
- }
39
-
40
- publish(transform) {
41
- if (this.panx) {
42
- const xdom = rescaleX(transform, this.xscale);
43
- this.xsel.update(this.clause(xdom, this.xfield, this.xscale));
44
- }
45
- if (this.pany) {
46
- const ydom = rescaleY(transform, this.yscale);
47
- this.ysel.update(this.clause(ydom, this.yfield, this.yscale));
48
- }
49
- }
50
-
51
- clause(value, field, scale) {
52
- return {
53
- source: this,
54
- schema: { type: 'interval', scales: [scale] },
55
- clients: this.mark.plot.markSet,
56
- value,
57
- predicate: value ? isBetween(field, value) : null
58
- };
59
- }
60
-
61
- init(svg) {
62
- this.svg = svg;
63
- if (this.initialized) return; else this.initialized = true;
64
-
65
- const { panx, pany, mark: { plot: { element } }, xsel, ysel } = this;
66
-
67
- this.xscale = svg.scale('x');
68
- this.yscale = svg.scale('y');
69
- const rx = this.xscale.range.slice().sort(asc);
70
- const ry = this.yscale.range.slice().sort(asc);
71
- const tx = extent(panx, [-Infinity, Infinity], rx);
72
- const ty = extent(pany, [-Infinity, Infinity], ry);
73
-
74
- const z = zoom()
75
- .extent([[rx[0], ry[0]], [rx[1], ry[1]]])
76
- .scaleExtent(this.zoom)
77
- .translateExtent([[tx[0], ty[0]], [tx[1], ty[1]]])
78
- .on('start', () => {
79
- this.xscale = this.svg.scale('x');
80
- this.yscale = this.svg.scale('y');
81
- })
82
- .on('end', () => element.__zoom = new ZoomTransform(1, 0, 0))
83
- .on('zoom', ({ transform }) => this.publish(transform));
84
-
85
- select(element).call(z);
86
-
87
- if (panx || pany) {
88
- let enter = false;
89
- element.addEventListener('mouseenter', () => {
90
- if (enter) return; else enter = true;
91
- if (panx) {
92
- const { xscale, xfield } = this;
93
- xsel.activate(this.clause(xscale.domain, xfield, xscale));
94
- }
95
- if (pany) {
96
- const { yscale, yfield } = this;
97
- ysel.activate(this.clause(yscale.domain, yfield, yscale));
98
- }
99
- });
100
- element.addEventListener('mouseleave', () => enter = false);
101
- }
102
- }
103
- }
104
-
105
- function extent(ext, defaultTrue, defaultFalse) {
106
- return ext
107
- ? (Array.isArray(ext) ? ext : defaultTrue)
108
- : defaultFalse;
109
- }
110
-
111
- function rescaleX(transform, scale) {
112
- return scale.range
113
- .map(transform.invertX, transform)
114
- .map(scale.invert, scale);
115
- }
116
-
117
- function rescaleY(transform, scale) {
118
- return scale.range
119
- .map(transform.invertY, transform)
120
- .map(scale.invert, scale);
121
- }
@@ -1,111 +0,0 @@
1
- import { and, or, isNotDistinct, literal } from '@uwdata/mosaic-sql';
2
-
3
- export class Toggle {
4
- constructor(mark, {
5
- selection,
6
- channels,
7
- peers = true
8
- }) {
9
- this.value = null;
10
- this.mark = mark;
11
- this.selection = selection;
12
- this.peers = peers;
13
- this.channels = channels.map(c => {
14
- const q = c === 'color' ? ['fill', 'stroke']
15
- : c === 'x' ? ['x', 'x1', 'x2']
16
- : c === 'y' ? ['y', 'y1', 'y2']
17
- : [c];
18
- for (let i = 0; i < q.length; ++i) {
19
- const f = mark.channelField(q[i]);
20
- if (f) return {
21
- field: f.field?.basis || f.field,
22
- as: f.as
23
- };
24
- }
25
- throw new Error(`Missing channel: ${c}`);
26
- });
27
- }
28
-
29
- clause(value) {
30
- const { channels, mark } = this;
31
- let predicate = null;
32
-
33
- if (value) {
34
- const clauses = value.map(vals => {
35
- const list = vals.map((v, i) => {
36
- return isNotDistinct(channels[i].field, literal(v));
37
- });
38
- return list.length > 1 ? and(list) : list[0];
39
- });
40
- predicate = clauses.length > 1 ? or(clauses) : clauses[0];
41
- }
42
-
43
- return {
44
- source: this,
45
- schema: { type: 'point' },
46
- clients: this.peers ? mark.plot.markSet : new Set().add(mark),
47
- value,
48
- predicate
49
- };
50
- }
51
-
52
- init(svg, selector, accessor) {
53
- const { mark, channels, selection } = this;
54
- const { data } = mark;
55
- accessor = accessor || (target => {
56
- const datum = data[target.__data__];
57
- return channels.map(c => datum[c.as]);
58
- });
59
-
60
- selector = selector || `[data-index="${mark.index}"]`;
61
- const groups = new Set(svg.querySelectorAll(selector));
62
-
63
- svg.addEventListener('pointerdown', evt => {
64
- const state = selection.single ? selection.value : this.value;
65
- const target = evt.target;
66
- let value = null;
67
-
68
- if (isTargetElement(groups, target)) {
69
- const point = accessor(target);
70
- if (evt.shiftKey && state?.length) {
71
- value = state.filter(s => neq(s, point));
72
- if (value.length === state.length) value.push(point);
73
- } else if (state?.length === 1 && !neq(state[0], point)) {
74
- value = null;
75
- } else {
76
- value = [point];
77
- }
78
- }
79
-
80
- this.value = value;
81
- if (neqSome(state, value)) {
82
- selection.update(this.clause(value));
83
- }
84
- });
85
-
86
- svg.addEventListener('pointerenter', () => {
87
- this.selection.activate(this.clause([this.channels.map(() => 0)]));
88
- });
89
- }
90
- }
91
-
92
- function isTargetElement(groups, node) {
93
- return groups.has(node)
94
- || groups.has(node.parentNode)
95
- || groups.has(node.parentNode?.parentNode);
96
- }
97
-
98
- function neqSome(a, b) {
99
- return (a == null || b == null)
100
- ? (a != null || b != null)
101
- : (a.length !== b.length || a.some((x, i) => neq(x, b[i])));
102
- }
103
-
104
- function neq(a, b) {
105
- const n = a.length;
106
- if (b.length !== n) return true;
107
- for (let i = 0; i < n; ++i) {
108
- if (a[i] !== b[i]) return true;
109
- }
110
- return false;
111
- }
@@ -1,45 +0,0 @@
1
- import {
2
- brush as d3_brush, brushX as d3_brushX, brushY as d3_brushY
3
- } from 'd3';
4
-
5
- function wrap(brush) {
6
- const brushOn = brush.on;
7
- let enabled = true;
8
-
9
- function silence(callback) {
10
- enabled = false;
11
- callback();
12
- enabled = true;
13
- }
14
-
15
- brush.reset = (...args) => {
16
- silence(() => brush.clear(...args));
17
- };
18
-
19
- brush.moveSilent = (...args) => {
20
- silence(() => brush.move(...args));
21
- };
22
-
23
- brush.on = (...args) => {
24
- if (args.length > 1 && args[1]) {
25
- // wrap callback to respect enabled flag
26
- const callback = args[1];
27
- args[1] = (...event) => enabled && callback(...event);
28
- }
29
- return brushOn(...args);
30
- };
31
-
32
- return brush;
33
- }
34
-
35
- export function brush() {
36
- return wrap(d3_brush());
37
- }
38
-
39
- export function brushX() {
40
- return wrap(d3_brushX());
41
- }
42
-
43
- export function brushY() {
44
- return wrap(d3_brushY());
45
- }
@@ -1,9 +0,0 @@
1
- const EPS = 1e-12;
2
-
3
- export function closeTo(a, b) {
4
- return a === b || (
5
- a && b &&
6
- Math.abs(a[0] - b[0]) < EPS &&
7
- Math.abs(a[1] - b[1]) < EPS
8
- ) || false;
9
- }
@@ -1,4 +0,0 @@
1
- export function getField(mark, channels) {
2
- const field = mark.channelField(channels)?.field;
3
- return field?.basis || field;
4
- }
@@ -1,3 +0,0 @@
1
- export function invert(value, scale, pixelSize = 1) {
2
- return scale.invert(pixelSize * Math.floor(value / pixelSize));
3
- }
@@ -1,13 +0,0 @@
1
- /**
2
- * Patch the getScreenCTM method to memoize the last non-null
3
- * result seen. This will let the method continue to function
4
- * even after the node is removed from the DOM.
5
- */
6
- export function patchScreenCTM() {
7
- const node = this;
8
- const getScreenCTM = node.getScreenCTM;
9
- let memo;
10
- node.getScreenCTM = () => {
11
- return node.isConnected ? (memo = getScreenCTM.call(node)) : memo;
12
- };
13
- }
@@ -1,9 +0,0 @@
1
- import { toKebabCase } from './to-kebab-case.js';
2
-
3
- export function sanitizeStyles(styles) {
4
- const s = {};
5
- for (const name in styles) {
6
- s[toKebabCase(name)] = styles[name];
7
- }
8
- return s;
9
- }
@@ -1,9 +0,0 @@
1
- export function toKebabCase(cc) {
2
- const lc = cc.toLowerCase();
3
- const n = cc.length;
4
- let kc = '';
5
- for (let i = 0; i < n; ++i) {
6
- kc += (cc[i] !== lc[i] ? '-' : '') + lc[i];
7
- }
8
- return kc;
9
- }
@@ -1,2 +0,0 @@
1
- export { hconcat, vconcat } from './concat.js';
2
- export { hspace, vspace } from './space.js';