@fjader/svelte 0.2.4 → 0.2.5
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/README.md +1 -1
- package/dist/animation/EntranceGate.d.ts +52 -0
- package/dist/animation/EntranceGate.js +93 -0
- package/dist/animation/TransitionClock.d.ts +61 -30
- package/dist/animation/TransitionClock.js +67 -29
- package/dist/animation/transition-log.d.ts +41 -0
- package/dist/animation/transition-log.js +57 -0
- package/dist/components/axes/AxisBottom.svelte +4 -2
- package/dist/components/axes/AxisCenter.svelte +4 -2
- package/dist/components/axes/AxisLeft.svelte +4 -2
- package/dist/components/axes/BandAxisTicks.svelte +3 -2
- package/dist/components/charts/ChartCell.svelte +32 -5
- package/dist/components/charts/ChartCell.svelte.d.ts +9 -0
- package/dist/components/charts/ChartContainer.svelte +45 -3
- package/dist/components/charts/ChartContainer.svelte.d.ts +7 -1
- package/dist/components/charts/ChartHover.svelte.d.ts +14 -0
- package/dist/components/charts/ChartHover.svelte.js +22 -0
- package/dist/components/charts/ChartVisibility.svelte.d.ts +43 -0
- package/dist/components/charts/ChartVisibility.svelte.js +67 -0
- package/dist/components/guides/Grid.svelte +12 -1
- package/dist/components/marks/AreaMarkX.svelte +16 -4
- package/dist/components/marks/AreaMarkY.svelte +16 -4
- package/dist/components/marks/BarMarkX.svelte +7 -3
- package/dist/components/marks/BarMarkY.svelte +7 -3
- package/dist/components/marks/DotsMark.svelte +6 -3
- package/dist/components/marks/LineMark.svelte +11 -8
- package/dist/components/marks/ParallelCoordinatesMark.svelte +7 -2
- package/dist/components/marks/ParallelCoordinatesMark.svelte.d.ts +2 -0
- package/dist/components/marks/RectMark.svelte +6 -3
- package/dist/components/marks/RectXMark.svelte +7 -3
- package/dist/components/marks/RectYMark.svelte +7 -3
- package/dist/components/marks/RuleMarkX.svelte +30 -26
- package/dist/components/marks/RuleMarkY.svelte +30 -26
- package/dist/components/marks/TextMark.svelte +44 -5
- package/dist/components/marks/lifecycle/MarkLifecycleV2.svelte.d.ts +16 -0
- package/dist/components/marks/lifecycle/MarkLifecycleV2.svelte.js +398 -54
- package/dist/components/marks/lifecycle/domain-queries.d.ts +11 -2
- package/dist/components/marks/lifecycle/domain-queries.js +33 -6
- package/dist/components/marks/lifecycle/domain-registration.d.ts +6 -1
- package/dist/components/marks/lifecycle/domain-registration.js +15 -14
- package/dist/components/marks/lifecycle/other-rows.svelte.d.ts +9 -1
- package/dist/components/marks/lifecycle/other-rows.svelte.js +2 -2
- package/dist/components/marks/lifecycle/query-builder.d.ts +27 -2
- package/dist/components/marks/lifecycle/query-builder.js +23 -20
- package/dist/components/marks/utils/MarkColor.svelte.d.ts +3 -1
- package/dist/components/marks/utils/MarkColor.svelte.js +33 -3
- package/dist/components/marks/utils/MarkSeries.svelte.d.ts +14 -1
- package/dist/components/marks/utils/MarkSeries.svelte.js +25 -1
- package/dist/components/marks/utils/MarkSize.svelte.js +4 -0
- package/dist/components/marks/utils/reactive-config.d.ts +1 -1
- package/dist/components/marks/utils/reactive-config.js +3 -0
- package/dist/components/rendering/Areas.svelte +57 -19
- package/dist/components/rendering/Areas.svelte.d.ts +12 -0
- package/dist/components/rendering/BaseGPURenderer.svelte.d.ts +1 -1
- package/dist/components/rendering/Dots.svelte +23 -3
- package/dist/components/rendering/Dots.svelte.d.ts +6 -0
- package/dist/components/rendering/GridLine.svelte +13 -1
- package/dist/components/rendering/GridLine.svelte.d.ts +5 -0
- package/dist/components/rendering/Lines.svelte +48 -13
- package/dist/components/rendering/Lines.svelte.d.ts +12 -0
- package/dist/components/rendering/ParallelLines.svelte +5 -5
- package/dist/components/rendering/Rects.svelte +51 -11
- package/dist/components/rendering/Rects.svelte.d.ts +16 -2
- package/dist/components/rendering/RuleX.svelte +1 -1
- package/dist/components/rendering/RuleY.svelte +1 -1
- package/dist/components/rendering/WebGPUCanvas.svelte +4 -2
- package/dist/components/rendering/activity-reads.d.ts +41 -24
- package/dist/components/rendering/activity-reads.js +54 -31
- package/dist/components/rendering/uniform-sizes.d.ts +1 -1
- package/dist/components/rendering/uniform-sizes.js +1 -1
- package/dist/config/ChartConfig.svelte.d.ts +7 -0
- package/dist/config/ChartConfig.svelte.js +17 -0
- package/dist/gpu/GPUCoordinator.svelte.d.ts +31 -1
- package/dist/gpu/GPUCoordinator.svelte.js +164 -7
- package/dist/gpu/MorphedChannels.d.ts +11 -2
- package/dist/gpu/MorphedChannels.js +33 -11
- package/dist/gpu/ScaleTemplateSystem.d.ts +20 -2
- package/dist/gpu/ScaleTemplateSystem.js +58 -2
- package/dist/index.d.ts +17 -4
- package/dist/index.js +19 -4
- package/dist/interactors/TooltipManager.svelte.js +14 -5
- package/dist/query-engine/CrossfilterSelection.svelte.d.ts +4 -2
- package/dist/query-engine/CrossfilterSelection.svelte.js +2 -0
- package/dist/query-engine/FieldActivity.svelte.d.ts +2 -3
- package/dist/query-engine/FieldActivity.svelte.js +1 -2
- package/dist/query-engine/MarkHandle.svelte.d.ts +28 -1
- package/dist/query-engine/MarkHandle.svelte.js +33 -1
- package/dist/query-engine/QueryEngine.d.ts +225 -5
- package/dist/query-engine/QueryEngine.js +671 -41
- package/dist/query-engine/categorical-identity.d.ts +34 -8
- package/dist/query-engine/categorical-identity.js +138 -73
- package/dist/query-engine/debug/query-log.d.ts +39 -0
- package/dist/query-engine/debug/query-log.js +90 -0
- package/dist/query-engine/duckdb.d.ts +55 -8
- package/dist/query-engine/duckdb.js +82 -30
- package/dist/query-engine/index.d.ts +7 -4
- package/dist/query-engine/index.js +3 -3
- package/dist/query-engine/preagg/BuildQueue.d.ts +33 -13
- package/dist/query-engine/preagg/BuildQueue.js +51 -15
- package/dist/query-engine/preagg/Preagg.d.ts +46 -3
- package/dist/query-engine/preagg/Preagg.js +159 -31
- package/dist/query-engine/preagg/plan.d.ts +28 -1
- package/dist/query-engine/preagg/plan.js +128 -8
- package/dist/query-engine/sliced-lane.d.ts +108 -0
- package/dist/query-engine/sliced-lane.js +299 -0
- package/dist/query-engine/source-routing.d.ts +106 -0
- package/dist/query-engine/source-routing.js +189 -0
- package/dist/scales/BandScaleState.svelte.d.ts +9 -1
- package/dist/scales/BandScaleState.svelte.js +16 -5
- package/dist/scales/ChartScales.svelte.d.ts +3 -0
- package/dist/scales/ChartScales.svelte.js +26 -8
- package/dist/scales/ColorScaleState.svelte.d.ts +18 -2
- package/dist/scales/ColorScaleState.svelte.js +138 -26
- package/dist/scales/LinearScaleState.svelte.d.ts +7 -10
- package/dist/scales/LinearScaleState.svelte.js +21 -39
- package/dist/scales/ScaleGeneration.svelte.d.ts +48 -0
- package/dist/scales/ScaleGeneration.svelte.js +42 -0
- package/dist/scales/ScaleRegistry.svelte.js +3 -1
- package/dist/scales/SlotTransition.d.ts +15 -1
- package/dist/scales/SlotTransition.js +33 -4
- package/dist/scales/TimeScaleState.svelte.d.ts +7 -3
- package/dist/scales/TimeScaleState.svelte.js +7 -7
- package/dist/scales/color-utils.d.ts +0 -3
- package/dist/scales/color-utils.js +0 -3
- package/dist/scales/entered.d.ts +8 -0
- package/dist/scales/entered.js +10 -0
- package/dist/spec/LazyQueryEngine.js +2 -19
- package/dist/spec/MarkRegistry.d.ts +3 -1
- package/dist/spec/MarkRegistry.js +13 -1
- package/dist/spec/PlotFromSpec.svelte +1 -0
- package/dist/spec/export.js +11 -2
- package/dist/spec/types.d.ts +41 -13
- package/dist/sql/bin.d.ts +25 -0
- package/dist/sql/bin.js +108 -5
- package/dist/transforms/TransformCompiler.d.ts +10 -0
- package/dist/transforms/TransformCompiler.js +26 -0
- package/dist/transforms/index.js +144 -24
- package/dist/types.d.ts +136 -2
- package/package.json +2 -2
package/dist/sql/bin.js
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
|
-
import { binDate, binHistogram, column } from '@uwdata/mosaic-sql';
|
|
1
|
+
import { binDate, binHistogram, binSpec, column, interval, sql, timeInterval } from '@uwdata/mosaic-sql';
|
|
2
2
|
import { isTemporalScale } from '../scales/scale-categories';
|
|
3
|
+
/**
|
|
4
|
+
* Numeric bins over [min, max]: binHistogram's own derivation (binSpec, with
|
|
5
|
+
* the linear transform it is given), as the first edge and the width.
|
|
6
|
+
*/
|
|
7
|
+
function numericBins(min, max, options) {
|
|
8
|
+
const b = binSpec(min, max, options);
|
|
9
|
+
return { min: b.min, alpha: (b.max - b.min) / b.steps || 1 };
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* The edge of numeric bin `index` (SQL), `min + alpha * (index + offset)`, in
|
|
13
|
+
* DOUBLE arithmetic. The one formula both `bin()` and `dense()`'s padding
|
|
14
|
+
* write, so a bin's edge is the same bits whichever produced it: a padded row
|
|
15
|
+
* then merges with a real one, and a mark's rows are the same bits whether
|
|
16
|
+
* they came from the base table or a preagg table.
|
|
17
|
+
*/
|
|
18
|
+
export function numericBinEdgeSQL(min, alpha, offset, index) {
|
|
19
|
+
return `(${sqlDouble(min)} + ${sqlDouble(alpha)} * (${index} + ${offset}))`;
|
|
20
|
+
}
|
|
21
|
+
/** A JS number as a DOUBLE literal: a bare 0.1 would be read as a DECIMAL. */
|
|
22
|
+
function sqlDouble(n) {
|
|
23
|
+
return `CAST(${JSON.stringify(n)} AS DOUBLE)`;
|
|
24
|
+
}
|
|
25
|
+
/** Engine-built SQL text as an expression node. Never user input. */
|
|
26
|
+
function sqlFragment(text) {
|
|
27
|
+
return sql([text]);
|
|
28
|
+
}
|
|
29
|
+
/** A time unit's typical length, for estimating a bucket count. */
|
|
30
|
+
const UNIT_MS = {
|
|
31
|
+
year: 365 * 86_400_000,
|
|
32
|
+
quarter: 91 * 86_400_000,
|
|
33
|
+
month: 30 * 86_400_000,
|
|
34
|
+
day: 86_400_000,
|
|
35
|
+
hour: 3_600_000,
|
|
36
|
+
minute: 60_000,
|
|
37
|
+
second: 1000,
|
|
38
|
+
millisecond: 1,
|
|
39
|
+
microsecond: 0.001
|
|
40
|
+
};
|
|
3
41
|
export function bin(field, options = {}) {
|
|
4
42
|
return new AutoBinNode(field, options);
|
|
5
43
|
}
|
|
@@ -19,6 +57,70 @@ export class AutoBinNode {
|
|
|
19
57
|
};
|
|
20
58
|
}
|
|
21
59
|
resolve(fieldInfo, scaleType, timeOffset = null, currentDomain = null, binStepMultiplier = 1.0) {
|
|
60
|
+
const { col, min, max, isTemporal, binOptions } = this.#plan(fieldInfo, scaleType, currentDomain, binStepMultiplier);
|
|
61
|
+
if (isTemporal)
|
|
62
|
+
return binDate(col, [min, max], binOptions);
|
|
63
|
+
// A single value: binHistogram floors it rather than binning.
|
|
64
|
+
if (max === min)
|
|
65
|
+
return binHistogram(col, [min, max], binOptions);
|
|
66
|
+
// binHistogram's bins, written with the formula dense() pads with, so a
|
|
67
|
+
// real row's edge and a padded row's are the same bits (see
|
|
68
|
+
// numericBinEdgeSQL).
|
|
69
|
+
const { min: lo, alpha } = numericBins(min, max, binOptions);
|
|
70
|
+
const index = `floor((CAST(${col} AS DOUBLE) - ${sqlDouble(lo)}) / ${sqlDouble(alpha)})`;
|
|
71
|
+
return sqlFragment(numericBinEdgeSQL(lo, alpha, binOptions.offset ?? 0, index));
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* The bins `resolve` produces with the same arguments: every bin a value of
|
|
75
|
+
* the field can fall in. What `dense()` pads a histogram from, so a bin
|
|
76
|
+
* emptied by a filter stays as a zero row and the bars can morph.
|
|
77
|
+
*
|
|
78
|
+
* Numeric bins as numbers, edge k at `min + alpha * (k + offset)`. Time
|
|
79
|
+
* buckets as the interval `time_bucket` steps by, and the field's extent;
|
|
80
|
+
* those need `t0`, the time scale's origin in epoch seconds, because a time
|
|
81
|
+
* channel is drawn as seconds from it, and are null without it (before the
|
|
82
|
+
* scale has one, the channel is not rewritten either).
|
|
83
|
+
*
|
|
84
|
+
* Null for a field with a single value, which binHistogram floors instead
|
|
85
|
+
* of binning.
|
|
86
|
+
*/
|
|
87
|
+
padding(fieldInfo, scaleType, currentDomain = null, binStepMultiplier = 1.0, t0 = null) {
|
|
88
|
+
const { col, min, max, isTemporal, binOptions } = this.#plan(fieldInfo, scaleType, currentDomain, binStepMultiplier);
|
|
89
|
+
const offset = binOptions.offset ?? 0;
|
|
90
|
+
const source = String(col);
|
|
91
|
+
if (isTemporal) {
|
|
92
|
+
if (t0 === null)
|
|
93
|
+
return null;
|
|
94
|
+
// binDate's own choice of unit and step.
|
|
95
|
+
const { unit, step = 1 } = binOptions.interval
|
|
96
|
+
? { unit: binOptions.interval, step: binOptions.step }
|
|
97
|
+
: timeInterval(min, max, binOptions.steps || 40);
|
|
98
|
+
const width = (UNIT_MS[unit] ?? UNIT_MS.day) * step;
|
|
99
|
+
return {
|
|
100
|
+
kind: 'time',
|
|
101
|
+
source,
|
|
102
|
+
interval: String(interval(unit, step)),
|
|
103
|
+
minMs: min,
|
|
104
|
+
maxMs: max,
|
|
105
|
+
t0,
|
|
106
|
+
count: Math.ceil((max - min) / width) + 2,
|
|
107
|
+
offset
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
if (max === min)
|
|
111
|
+
return null;
|
|
112
|
+
const { min: lo, alpha } = numericBins(min, max, binOptions);
|
|
113
|
+
return {
|
|
114
|
+
kind: 'number',
|
|
115
|
+
source,
|
|
116
|
+
min: lo,
|
|
117
|
+
alpha,
|
|
118
|
+
count: Math.floor((max - lo) / alpha) + 1,
|
|
119
|
+
offset
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
/** The column, extent and options both `resolve` and `padding` bin with. */
|
|
123
|
+
#plan(fieldInfo, scaleType, currentDomain, binStepMultiplier) {
|
|
22
124
|
const fieldName = typeof this.field === 'string' ? this.field : this.field.toString();
|
|
23
125
|
const info = fieldInfo[fieldName] || {};
|
|
24
126
|
const { min, max, type } = info;
|
|
@@ -50,12 +152,13 @@ export class AutoBinNode {
|
|
|
50
152
|
binOptions.steps = Math.max(1, Math.round(binOptions.steps * binStepMultiplier));
|
|
51
153
|
}
|
|
52
154
|
if (binOptions.step) {
|
|
53
|
-
|
|
155
|
+
// A time interval counts whole units: `INTERVAL 6.78 day` is not SQL.
|
|
156
|
+
binOptions.step = binOptions.interval
|
|
157
|
+
? Math.max(1, Math.round(binOptions.step / binStepMultiplier))
|
|
158
|
+
: binOptions.step / binStepMultiplier;
|
|
54
159
|
}
|
|
55
160
|
}
|
|
56
|
-
return isTemporal
|
|
57
|
-
? binDate(col, [min, max], binOptions)
|
|
58
|
-
: binHistogram(col, [min, max], binOptions);
|
|
161
|
+
return { col, min, max, isTemporal, binOptions };
|
|
59
162
|
}
|
|
60
163
|
withOffset(offset) {
|
|
61
164
|
return new AutoBinNode(this.field, {
|
|
@@ -24,6 +24,16 @@ export declare class TransformCompiler {
|
|
|
24
24
|
options?: {
|
|
25
25
|
skipRowFilters?: boolean;
|
|
26
26
|
}): SelectQuery;
|
|
27
|
+
/**
|
|
28
|
+
* The padding rows of this mark's queries (see SqlTransform.padRows), for a
|
|
29
|
+
* pre-aggregated table to hold, or null when they cannot live in a table.
|
|
30
|
+
*
|
|
31
|
+
* Only when padding is the last thing that changes which rows exist: every
|
|
32
|
+
* other SQL transform is one that runs the same over a padded table as over
|
|
33
|
+
* a padded result. A topN ranks before padding, so zero rows in its table
|
|
34
|
+
* would enter the ranking; anything else not listed is not known to be safe.
|
|
35
|
+
*/
|
|
36
|
+
padRows(aliases: string[], facts?: Partial<MarkConfig>): string | null;
|
|
27
37
|
/**
|
|
28
38
|
* Whether a SQL transform filters on the groups' aggregates (`having`). Its
|
|
29
39
|
* condition names aggregates over the source rows, which a pre-aggregated table
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQL transforms that give the same rows over a table holding its padding as
|
|
3
|
+
* padding gives their result: row filters (applied to the table's build), and
|
|
4
|
+
* what runs after padding (rank, stack, normalize).
|
|
5
|
+
*/
|
|
6
|
+
const PAD_SAFE = new Set(['dense', 'filter', 'rank', 'stack', 'normalize']);
|
|
1
7
|
export class TransformCompiler {
|
|
2
8
|
transforms;
|
|
3
9
|
sqlTransforms;
|
|
@@ -52,6 +58,26 @@ export class TransformCompiler {
|
|
|
52
58
|
}
|
|
53
59
|
return result;
|
|
54
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* The padding rows of this mark's queries (see SqlTransform.padRows), for a
|
|
63
|
+
* pre-aggregated table to hold, or null when they cannot live in a table.
|
|
64
|
+
*
|
|
65
|
+
* Only when padding is the last thing that changes which rows exist: every
|
|
66
|
+
* other SQL transform is one that runs the same over a padded table as over
|
|
67
|
+
* a padded result. A topN ranks before padding, so zero rows in its table
|
|
68
|
+
* would enter the ranking; anything else not listed is not known to be safe.
|
|
69
|
+
*/
|
|
70
|
+
padRows(aliases, facts) {
|
|
71
|
+
if (!this.sqlTransforms.every((t) => PAD_SAFE.has(t.type)))
|
|
72
|
+
return null;
|
|
73
|
+
const config = this.#configFor(facts);
|
|
74
|
+
for (const transform of this.sqlTransforms) {
|
|
75
|
+
const rows = transform.padRows?.(aliases, config);
|
|
76
|
+
if (rows)
|
|
77
|
+
return rows;
|
|
78
|
+
}
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
55
81
|
/**
|
|
56
82
|
* Whether a SQL transform filters on the groups' aggregates (`having`). Its
|
|
57
83
|
* condition names aggregates over the source rows, which a pre-aggregated table
|
package/dist/transforms/index.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* - JS: Process data via the DataAccessor interface (zero-copy on Arrow data)
|
|
7
7
|
*/
|
|
8
8
|
import { Query, column, sql as sqlTag } from '@uwdata/mosaic-sql';
|
|
9
|
+
import { numericBinEdgeSQL } from '../sql/bin';
|
|
9
10
|
// =============================================================================
|
|
10
11
|
// SQL TRANSFORMS (Query Modifiers)
|
|
11
12
|
// =============================================================================
|
|
@@ -208,18 +209,29 @@ const BAND_SOURCE_COLUMN = '__dplot_band_src';
|
|
|
208
209
|
* instead, and the query assembler orders the outermost query by it.
|
|
209
210
|
*/
|
|
210
211
|
function densify(inner, aliases, plan) {
|
|
211
|
-
const { band, dim, cross } = plan;
|
|
212
|
+
const { band, dim, cross, bins } = plan;
|
|
212
213
|
const crossAlias = new Map(cross.map(([key], i) => [key, `k${i}`]));
|
|
214
|
+
const binOf = new Map(bins);
|
|
213
215
|
const select = {};
|
|
214
216
|
for (const alias of aliases) {
|
|
215
217
|
const crossed = crossAlias.get(alias);
|
|
216
|
-
|
|
217
|
-
|
|
218
|
+
const bin = binOf.get(alias);
|
|
219
|
+
if (bin) {
|
|
220
|
+
// Every row's edge from its bin index, real and padded alike, so the
|
|
221
|
+
// edges are the same bits on every update: they are what tells the
|
|
222
|
+
// renderer the row set has not changed (alignmentKey).
|
|
223
|
+
select[alias] = rawSql(binEdge(bin, 'd.k'));
|
|
224
|
+
}
|
|
225
|
+
else if (alias === band) {
|
|
226
|
+
// REAL, as the inner query's id columns are: an INTEGER column would
|
|
227
|
+
// reach the GPU through a copy, and hand the renderer other bits than
|
|
228
|
+
// a preagg read, which keeps the REAL ids.
|
|
229
|
+
select[alias] = rawSql(`CAST(d."id" AS REAL)`);
|
|
218
230
|
}
|
|
219
231
|
else if (crossed) {
|
|
220
232
|
// Another group key, enumerated from its own dim: the padded row for a
|
|
221
233
|
// missing (category, series) pair carries that series' real id.
|
|
222
|
-
select[alias] = rawSql(
|
|
234
|
+
select[alias] = rawSql(`CAST(${crossed}."id" AS REAL)`);
|
|
223
235
|
}
|
|
224
236
|
else if (alias.startsWith(OTHER_MEMBER_PREFIX)) {
|
|
225
237
|
// Other's member lists: NULL on a padded row, as on every row but
|
|
@@ -227,7 +239,7 @@ function densify(inner, aliases, plan) {
|
|
|
227
239
|
// a list.
|
|
228
240
|
select[alias] = rawSql(`a.${column(alias)}`);
|
|
229
241
|
}
|
|
230
|
-
else if (plan.identity[alias]?.dim === dim) {
|
|
242
|
+
else if (!bins && plan.identity[alias]?.dim === dim) {
|
|
231
243
|
// Same dim as the band, so this channel IS the category — the ordinary
|
|
232
244
|
// "colour the bars by their own category" chart. Its value for a row
|
|
233
245
|
// with no data is not unknown, it is that category's id, so take it
|
|
@@ -236,7 +248,7 @@ function densify(inner, aliases, plan) {
|
|
|
236
248
|
// This is what keeps a DEPARTING bar its colour. It is still on screen
|
|
237
249
|
// while it shrinks, and a sentinel would turn it grey on the frame its
|
|
238
250
|
// data disappeared — the one frame where the colour matters most.
|
|
239
|
-
select[alias] =
|
|
251
|
+
select[alias] = rawSql(`CAST(d."id" AS REAL)`);
|
|
240
252
|
}
|
|
241
253
|
else if (plan.identity[alias]) {
|
|
242
254
|
// A category id from some OTHER field that is not a group key — an
|
|
@@ -244,7 +256,7 @@ function densify(inner, aliases, plan) {
|
|
|
244
256
|
// so the absent sentinel it is. Never 0, which is a real category: it
|
|
245
257
|
// would enter the colour scale's domain, shift every palette rank after
|
|
246
258
|
// it, and repaint bars that had not changed.
|
|
247
|
-
select[alias] = sqlTag `COALESCE(a.${column(alias)}, -1)`;
|
|
259
|
+
select[alias] = sqlTag `COALESCE(a.${column(alias)}, CAST(-1 AS REAL))`;
|
|
248
260
|
}
|
|
249
261
|
else {
|
|
250
262
|
// A measure. 0 is the point: it is what makes a departing bar shrink to
|
|
@@ -275,11 +287,52 @@ function densify(inner, aliases, plan) {
|
|
|
275
287
|
const joins = cross
|
|
276
288
|
.map(([key, keyDim], i) => ` CROSS JOIN (SELECT "id" FROM ${padIds(plan.identity[key], keyDim)} UNION ALL SELECT -1) AS k${i}`)
|
|
277
289
|
.join('');
|
|
278
|
-
const
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
return Query.from(rawSql(`${
|
|
290
|
+
const bandBin = binOf.get(band);
|
|
291
|
+
const bandOn = bandBin ? binMatch(bandBin, `a.${column(band)}`) : `a.${column(band)} = d."id"`;
|
|
292
|
+
const on = [bandOn, ...cross.map(([key], i) => `a.${column(key)} = k${i}."id"`)].join(' AND ');
|
|
293
|
+
const source = bandBin ? binSource(bandBin) : `${padIds(plan.identity[band], dim)} AS d`;
|
|
294
|
+
return Query.from(rawSql(`${source}${joins} LEFT JOIN (${inner}) AS a ON ${on}`)).select(select);
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Every bin, as `d.k`: an index for numeric bins, a bucket start for time
|
|
298
|
+
* buckets. Time buckets come from `time_bucket` itself over the field's
|
|
299
|
+
* extent, so they are exactly the buckets the query's own expression makes,
|
|
300
|
+
* months and years included, which are not a fixed width.
|
|
301
|
+
*/
|
|
302
|
+
function binSource(bin) {
|
|
303
|
+
if (bin.kind === 'number')
|
|
304
|
+
return `range(0, ${bin.count}) AS d(k)`;
|
|
305
|
+
const first = `time_bucket(${bin.interval}, epoch_ms(${Math.round(bin.minMs)}))`;
|
|
306
|
+
const last = `time_bucket(${bin.interval}, epoch_ms(${Math.round(bin.maxMs)}))`;
|
|
307
|
+
return `range(${first}, ${last} + ${bin.interval}, ${bin.interval}) AS d(k)`;
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Bin `k`'s edge as the REAL the query draws. Numeric: `min + alpha * (k +
|
|
311
|
+
* offset)`, in DOUBLE arithmetic. Time: the bucket start plus `offset`
|
|
312
|
+
* intervals, as seconds from `t0`, the form the mark's time channels take.
|
|
313
|
+
*/
|
|
314
|
+
function binEdge(bin, k) {
|
|
315
|
+
if (bin.kind === 'number') {
|
|
316
|
+
return `CAST(${numericBinEdgeSQL(bin.min, bin.alpha, bin.offset, k)} AS REAL)`;
|
|
317
|
+
}
|
|
318
|
+
return `CAST((epoch(${k} + (${bin.interval}) * ${bin.offset}) - ${bin.t0}) AS REAL)`;
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Whether the result row's edge `edge` is bin `d.k`. Numeric bins match by
|
|
322
|
+
* index, recovered from the edge: the edge is a REAL in the result (and in a
|
|
323
|
+
* preagg table), so comparing it to an edge computed here could miss by a
|
|
324
|
+
* rounding. A time edge is computed by the same expression on both sides, so
|
|
325
|
+
* the REALs are equal.
|
|
326
|
+
*/
|
|
327
|
+
function binMatch(bin, edge) {
|
|
328
|
+
if (bin.kind === 'number') {
|
|
329
|
+
return `round((CAST(${edge} AS DOUBLE) - ${sqlNumber(bin.min)}) / ${sqlNumber(bin.alpha)}) - ${bin.offset} = d.k`;
|
|
330
|
+
}
|
|
331
|
+
return `${edge} = ${binEdge(bin, 'd.k')}`;
|
|
332
|
+
}
|
|
333
|
+
/** A JS number as a DOUBLE literal: a bare 0.1 would be read as a DECIMAL. */
|
|
334
|
+
function sqlNumber(n) {
|
|
335
|
+
return `CAST(${JSON.stringify(n)} AS DOUBLE)`;
|
|
283
336
|
}
|
|
284
337
|
/**
|
|
285
338
|
* Most padded rows `dense()` will produce before refusing.
|
|
@@ -347,20 +400,24 @@ function densePlan(aliases, config, enabled, reported) {
|
|
|
347
400
|
if (reported.has(key))
|
|
348
401
|
return;
|
|
349
402
|
reported.add(key);
|
|
350
|
-
console.info(`[dense] ${reason} Categories on this mark will snap between updates ` +
|
|
351
|
-
`rather than morphing, and
|
|
403
|
+
console.info(`[dense] ${reason} Categories or bins on this mark will snap between updates ` +
|
|
404
|
+
`rather than morphing, and one leaving will vanish rather than shrink out.`);
|
|
352
405
|
};
|
|
353
406
|
if (!enabled)
|
|
354
407
|
return null;
|
|
355
|
-
const identity = config?.identity;
|
|
356
|
-
if (!identity)
|
|
357
|
-
return null;
|
|
358
408
|
// Stated by whoever built the select, because it cannot be recovered from
|
|
359
409
|
// the query by the time this runs: topN has already wrapped it, and the
|
|
360
410
|
// GROUP BY is several levels down.
|
|
361
411
|
const groupKeys = config?.groupKeys;
|
|
362
412
|
if (!groupKeys)
|
|
363
413
|
return null;
|
|
414
|
+
// A histogram: its bins are enumerable like a dim's ids.
|
|
415
|
+
const binned = binPlan(aliases, groupKeys, config, say);
|
|
416
|
+
if (binned !== undefined)
|
|
417
|
+
return binned;
|
|
418
|
+
const identity = config?.identity;
|
|
419
|
+
if (!identity)
|
|
420
|
+
return null;
|
|
364
421
|
const candidates = aliases.filter((ch) => identity[ch]?.positional);
|
|
365
422
|
if (candidates.length !== 1) {
|
|
366
423
|
if (candidates.length > 1) {
|
|
@@ -400,7 +457,59 @@ function densePlan(aliases, config, enabled, reported) {
|
|
|
400
457
|
`(${size} categories${cross.length > 0 ? ` across ${cross.length + 1} group keys` : ''}).`);
|
|
401
458
|
return null;
|
|
402
459
|
}
|
|
403
|
-
return { band, dim, cross, identity };
|
|
460
|
+
return { band, dim, cross, bins: null, identity };
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* The plan for a binned axis: one row per bin, from the bins `bin()`
|
|
464
|
+
* resolved, crossed with each other group key's ids as for a categorical band.
|
|
465
|
+
* Null with nothing binned. Refuses, saying why, what it cannot enumerate: bins
|
|
466
|
+
* on both axes (a 2D histogram) or beside a categorical axis, a group key with
|
|
467
|
+
* no dim, and too many rows. Silent while identity has not landed for a group
|
|
468
|
+
* key that will get it.
|
|
469
|
+
*/
|
|
470
|
+
function binPlan(aliases, groupKeys, config, say) {
|
|
471
|
+
const specs = config?.bins;
|
|
472
|
+
if (!specs)
|
|
473
|
+
return undefined;
|
|
474
|
+
const bins = aliases
|
|
475
|
+
.filter((ch) => specs[ch] && groupKeys.includes(ch))
|
|
476
|
+
.map((ch) => [ch, specs[ch]])
|
|
477
|
+
.sort((a, b) => a[1].offset - b[1].offset);
|
|
478
|
+
if (bins.length === 0)
|
|
479
|
+
return undefined;
|
|
480
|
+
const identity = config?.identity ?? null;
|
|
481
|
+
if (new Set(bins.map(([ch]) => ch[0])).size > 1) {
|
|
482
|
+
say('Bins on both axes (a 2D histogram), and padding is defined for one.');
|
|
483
|
+
return null;
|
|
484
|
+
}
|
|
485
|
+
const categorical = identity ? aliases.filter((ch) => identity[ch]?.positional) : [];
|
|
486
|
+
if (categorical.length > 0) {
|
|
487
|
+
say(`A binned axis beside a categorical one (${categorical.join(', ')}).`);
|
|
488
|
+
return null;
|
|
489
|
+
}
|
|
490
|
+
const binned = new Set(bins.map(([ch]) => ch));
|
|
491
|
+
const cross = [];
|
|
492
|
+
let rows = bins[0][1].count;
|
|
493
|
+
for (const key of groupKeys) {
|
|
494
|
+
if (binned.has(key) || !aliases.includes(key))
|
|
495
|
+
continue;
|
|
496
|
+
const keyIdentity = identity?.[key];
|
|
497
|
+
// Before identity setup a series has no dim yet; it will.
|
|
498
|
+
if (!identity)
|
|
499
|
+
return null;
|
|
500
|
+
if (!keyIdentity) {
|
|
501
|
+
say(`the group key "${key}" has no dim, so its missing combinations cannot be enumerated.`);
|
|
502
|
+
return null;
|
|
503
|
+
}
|
|
504
|
+
cross.push([key, keyIdentity.dim]);
|
|
505
|
+
rows *= keyIdentity.size + 1;
|
|
506
|
+
}
|
|
507
|
+
if (rows > DENSE_ROW_LIMIT) {
|
|
508
|
+
say(`padding would produce about ${rows} rows, over the ${DENSE_ROW_LIMIT} limit ` +
|
|
509
|
+
`(${bins[0][1].count} bins${cross.length > 0 ? ` across ${cross.length + 1} group keys` : ''}).`);
|
|
510
|
+
return null;
|
|
511
|
+
}
|
|
512
|
+
return { band: bins[0][0], dim: '', cross, bins, identity: identity ?? {} };
|
|
404
513
|
}
|
|
405
514
|
/**
|
|
406
515
|
* Return a row for every category, not only the ones that survived.
|
|
@@ -440,10 +549,27 @@ export function dense(options = {}) {
|
|
|
440
549
|
return plan ? [plan.band, ...plan.cross.map(([key]) => key)] : null;
|
|
441
550
|
},
|
|
442
551
|
apply(query, config) {
|
|
552
|
+
// A preagg table built with padRows() below: its rows are already one
|
|
553
|
+
// per category or bin, and only the order is left, which the query's
|
|
554
|
+
// ORDER BY takes from alignment() as for any other read.
|
|
555
|
+
if (config?.padded)
|
|
556
|
+
return query;
|
|
443
557
|
const aliases = getSelectAliases(query);
|
|
444
558
|
const plan = densePlan(aliases, config, enabled, reported);
|
|
445
559
|
return plan ? densify(query, aliases, plan) : query;
|
|
446
560
|
},
|
|
561
|
+
/**
|
|
562
|
+
* The rows apply() would add, as the padding of an empty result: the same
|
|
563
|
+
* join and the same rules for every channel, so a table built with them
|
|
564
|
+
* holds exactly what padding its reads would have added.
|
|
565
|
+
*/
|
|
566
|
+
padRows(aliases, config) {
|
|
567
|
+
const plan = densePlan(aliases, config, enabled, reported);
|
|
568
|
+
if (!plan)
|
|
569
|
+
return null;
|
|
570
|
+
const empty = Query.select(Object.fromEntries(aliases.map((alias) => [alias, rawSql('NULL')]))).where(rawSql('false'));
|
|
571
|
+
return String(densify(empty, aliases, plan));
|
|
572
|
+
},
|
|
447
573
|
describe() {
|
|
448
574
|
return 'Dense categories';
|
|
449
575
|
}
|
|
@@ -970,12 +1096,6 @@ function stackXJS(options) {
|
|
|
970
1096
|
}
|
|
971
1097
|
};
|
|
972
1098
|
}
|
|
973
|
-
/**
|
|
974
|
-
* Core stacking computation on DataAccessor columns.
|
|
975
|
-
*
|
|
976
|
-
* Single O(n) pass: detects group boundaries in the sorted group column,
|
|
977
|
-
* accumulates value offsets, writes lo/hi Float32Arrays, applies offset mode.
|
|
978
|
-
*/
|
|
979
1099
|
/**
|
|
980
1100
|
* Core stacking computation on DataAccessor columns.
|
|
981
1101
|
*
|
package/dist/types.d.ts
CHANGED
|
@@ -39,13 +39,27 @@ export type FieldRef = string | ExprNode;
|
|
|
39
39
|
* resolves against the chart's state, such as bin() or a rolling average.
|
|
40
40
|
*/
|
|
41
41
|
export type ChannelRef = FieldRef | AutoBinNode | RollingAvgNode;
|
|
42
|
-
/**
|
|
42
|
+
/** A category value a colour can be pinned to. */
|
|
43
|
+
export type ColorDomainValue = string | number | boolean | null;
|
|
44
|
+
/**
|
|
45
|
+
* Color prop: CSS constant, column name, object wrapper, SQL node, or null.
|
|
46
|
+
*
|
|
47
|
+
* The object wrapper takes a scheme and type, or explicit ordinal colours:
|
|
48
|
+
*
|
|
49
|
+
* { field: 'won', domain: [0, 1], range: ['#d62728', '#2ca02c'] }
|
|
50
|
+
*
|
|
51
|
+
* `range` replaces the scheme's colours, which go to the values in sorted
|
|
52
|
+
* order. `domain` pins values to them instead: domain[i] draws range[i], and
|
|
53
|
+
* values outside it draw the null colour. Either one makes the scale ordinal.
|
|
54
|
+
*/
|
|
43
55
|
export type ColorProp = FieldRef | {
|
|
44
56
|
field: string;
|
|
45
57
|
scheme?: ColorScheme;
|
|
46
58
|
type?: ColorScaleType;
|
|
47
59
|
constant?: number;
|
|
48
60
|
exponent?: number;
|
|
61
|
+
domain?: readonly ColorDomainValue[];
|
|
62
|
+
range?: readonly string[];
|
|
49
63
|
} | null;
|
|
50
64
|
/**
|
|
51
65
|
* A SQL transform modifies the Mosaic Query AST before execution.
|
|
@@ -77,6 +91,12 @@ export interface SqlTransform {
|
|
|
77
91
|
* band a mark may morph along.
|
|
78
92
|
*/
|
|
79
93
|
alignment?(aliases: string[], config?: MarkConfig): string[] | null;
|
|
94
|
+
/**
|
|
95
|
+
* The rows this transform would add to a result to pad it, as a SELECT of
|
|
96
|
+
* every alias, or null when it pads nothing. A pre-aggregated table is
|
|
97
|
+
* built with them, so its reads need no padding of their own (`padded`).
|
|
98
|
+
*/
|
|
99
|
+
padRows?(aliases: string[], config?: MarkConfig): string | null;
|
|
80
100
|
describe?(): string;
|
|
81
101
|
}
|
|
82
102
|
/** A JS transform processes client-side data after the query returns. */
|
|
@@ -119,6 +139,40 @@ export interface ChannelIdentity {
|
|
|
119
139
|
*/
|
|
120
140
|
dropNullId?: number;
|
|
121
141
|
}
|
|
142
|
+
/**
|
|
143
|
+
* One binned channel's bins, as `bin()` resolved them for a query, which
|
|
144
|
+
* `dense()` pads a histogram from. A rect's upper edge is the same bins with
|
|
145
|
+
* offset 1.
|
|
146
|
+
*/
|
|
147
|
+
export type BinPadding = NumericBinPadding | TimeBinPadding;
|
|
148
|
+
/** Numeric bins: edge k is `min + alpha * (k + offset)` for k in [0, count). */
|
|
149
|
+
export interface NumericBinPadding {
|
|
150
|
+
kind: 'number';
|
|
151
|
+
/** The binned column or expression, as SQL: a bin is NULL exactly where it is. */
|
|
152
|
+
source: string;
|
|
153
|
+
min: number;
|
|
154
|
+
alpha: number;
|
|
155
|
+
count: number;
|
|
156
|
+
offset: number;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Time buckets of `interval` (SQL, as `time_bucket` takes it) over the field's
|
|
160
|
+
* extent, minMs to maxMs. An edge is the bucket start plus `offset` intervals,
|
|
161
|
+
* drawn as seconds from the time scale's origin `t0` (epoch seconds), the form
|
|
162
|
+
* a time channel takes in the query. `count` estimates the buckets, for the
|
|
163
|
+
* row limit: months and years are not a fixed width.
|
|
164
|
+
*/
|
|
165
|
+
export interface TimeBinPadding {
|
|
166
|
+
kind: 'time';
|
|
167
|
+
/** The binned column or expression, as SQL: a bucket is NULL exactly where it is. */
|
|
168
|
+
source: string;
|
|
169
|
+
interval: string;
|
|
170
|
+
minMs: number;
|
|
171
|
+
maxMs: number;
|
|
172
|
+
t0: number;
|
|
173
|
+
count: number;
|
|
174
|
+
offset: number;
|
|
175
|
+
}
|
|
122
176
|
export interface MarkConfig {
|
|
123
177
|
markType: string;
|
|
124
178
|
table: string;
|
|
@@ -129,6 +183,10 @@ export interface MarkConfig {
|
|
|
129
183
|
identity?: Record<string, ChannelIdentity>;
|
|
130
184
|
/** The query's GROUP BY channels, or null when it does not aggregate. Per query. */
|
|
131
185
|
groupKeys?: string[] | null;
|
|
186
|
+
/** The numeric `bin()` channels and their bins, which `dense()` pads from. Per query. */
|
|
187
|
+
bins?: Record<string, BinPadding> | null;
|
|
188
|
+
/** The query reads a table that already holds its padding rows. Per query. */
|
|
189
|
+
padded?: boolean;
|
|
132
190
|
[key: string]: unknown;
|
|
133
191
|
}
|
|
134
192
|
export interface ScaleMap {
|
|
@@ -198,6 +256,32 @@ export interface BaseMarkProps {
|
|
|
198
256
|
* - `'filter'`: every selection filters it, even on a field it draws by, so
|
|
199
257
|
* its aggregates are recomputed for the subset: top N within the selected
|
|
200
258
|
* region, shares within the selected groups.
|
|
259
|
+
* - `'ignore'`: no selection filters or dims it, its own chart's controls
|
|
260
|
+
* included, and a selection change never re-queries it. For context: the
|
|
261
|
+
* whole drawn faintly behind a filtered part, on the same scales, whose
|
|
262
|
+
* extent then keeps the axis still while the part changes. A click on its
|
|
263
|
+
* Other bar toggles, as under `'auto'`.
|
|
264
|
+
*
|
|
265
|
+
* Background totals are two layers of the same mark: an `'ignore'` one
|
|
266
|
+
* drawn faintly behind, the whole, and an `'auto'` one in front, the part a
|
|
267
|
+
* brush or click elsewhere leaves. Each bar shrinks over its total instead
|
|
268
|
+
* of losing it, and the y axis keeps the total's extent:
|
|
269
|
+
*
|
|
270
|
+
* ```svelte
|
|
271
|
+
* <BarMarkY table="flights" x="carrier" y={count()} selectionResponse="ignore" fillOpacity={0.2} />
|
|
272
|
+
* <BarMarkY table="flights" x="carrier" y={count()} />
|
|
273
|
+
* ```
|
|
274
|
+
*
|
|
275
|
+
* With a top N, choose which layer ranks; the other gets
|
|
276
|
+
* `contributeDomain={false}` and follows the axis:
|
|
277
|
+
*
|
|
278
|
+
* - the back layer, to rank once over the whole: the same N categories
|
|
279
|
+
* stay while the front bars shrink;
|
|
280
|
+
* - the front layer, to re-rank within the selection: the N categories
|
|
281
|
+
* change with it, and the whole is drawn for whichever are shown.
|
|
282
|
+
*
|
|
283
|
+
* Not with `includeOthers`: the follower has no Other row (see
|
|
284
|
+
* `contributeDomain`).
|
|
201
285
|
*
|
|
202
286
|
* A `'filter'` mark never dims. A legend, Toggle or band label filters it
|
|
203
287
|
* in its own chart too: every such control on the same fields shares one
|
|
@@ -237,12 +321,32 @@ export interface BaseMarkProps {
|
|
|
237
321
|
* before any transform, so a top N ranks, and Other sums, without them.
|
|
238
322
|
*/
|
|
239
323
|
nulls?: NullsResponse;
|
|
324
|
+
/**
|
|
325
|
+
* Whether the chart's entrance waits for this mark's first data. Default
|
|
326
|
+
* true: the chart enters once every mark has landed, so later marks cannot
|
|
327
|
+
* widen the axes mid-entrance. False for a mark known to be slower than
|
|
328
|
+
* the rest: the chart enters without it, and it joins the entrance where it
|
|
329
|
+
* is, or appears after it. A chart waits at most a second past its first
|
|
330
|
+
* mark either way.
|
|
331
|
+
*/
|
|
332
|
+
holdsEntrance?: boolean;
|
|
240
333
|
scheme?: ColorScheme;
|
|
241
334
|
limit?: number;
|
|
242
335
|
transforms?: Transform[];
|
|
243
336
|
sort?: SortConfig | null;
|
|
244
337
|
domain?: DomainConfig | null;
|
|
245
|
-
|
|
338
|
+
/**
|
|
339
|
+
* Hover tooltips. `true` (the default) or `false`, or options:
|
|
340
|
+
*
|
|
341
|
+
* - `sortTable`: write the mark's tooltip table sorted by x, so a hover
|
|
342
|
+
* lookup can skip DuckDB row groups outside the pointer's box. Off by
|
|
343
|
+
* default: the sort delays the first hover (the table is written when the
|
|
344
|
+
* pointer enters the chart), and measured up to 2M rows it did not make
|
|
345
|
+
* lookups faster.
|
|
346
|
+
*/
|
|
347
|
+
tooltip?: boolean | {
|
|
348
|
+
sortTable?: boolean;
|
|
349
|
+
};
|
|
246
350
|
tooltipProximity?: 'x' | 'y' | 'xy';
|
|
247
351
|
tip?: (FieldRef | '*')[] | false;
|
|
248
352
|
fitDomain?: 'x' | 'y' | boolean;
|
|
@@ -313,6 +417,18 @@ export interface LODConfig {
|
|
|
313
417
|
/** Callback fired on LOD state transitions (decimation toggle, dots appear/disappear). */
|
|
314
418
|
onLODChange?: (state: LODChangeState) => void;
|
|
315
419
|
}
|
|
420
|
+
/**
|
|
421
|
+
* How a chart's marks enter when its data first draws.
|
|
422
|
+
*
|
|
423
|
+
* - `'auto'` (the default): bars, histograms and areas grow from 0 on their
|
|
424
|
+
* measure axis, showing magnitude; everything else fades in.
|
|
425
|
+
* - `'fade'`: every mark fades in. Calmer, for dense small multiples.
|
|
426
|
+
* - `'none'`: marks appear at once.
|
|
427
|
+
*
|
|
428
|
+
* Under `prefers-reduced-motion: reduce`, `'auto'` becomes `'fade'`. The axes
|
|
429
|
+
* never move on load: the entrance is the data's, not the viewport's.
|
|
430
|
+
*/
|
|
431
|
+
export type EntranceMode = 'auto' | 'fade' | 'none';
|
|
316
432
|
import type { Margin, Point, AxesResult } from './layout/ChartLayout.svelte';
|
|
317
433
|
import type { LinearScaleState } from './scales/LinearScaleState.svelte';
|
|
318
434
|
import type { BandScaleState } from './scales/BandScaleState.svelte';
|
|
@@ -372,6 +488,24 @@ export interface ChartContext {
|
|
|
372
488
|
readonly currentXScreenPoint: number | null | undefined;
|
|
373
489
|
readonly currentYScreenPoint: number | null | undefined;
|
|
374
490
|
readonly xScaleState: ScaleStateInstance | null | undefined;
|
|
491
|
+
/**
|
|
492
|
+
* How many times the chart's x or y scale has been replaced by another (not
|
|
493
|
+
* its first resolution). Marks rebuild their lifecycle when it changes.
|
|
494
|
+
*/
|
|
495
|
+
readonly scaleGeneration: number;
|
|
496
|
+
/**
|
|
497
|
+
* Whether any of the chart's cells is on screen (with a small margin).
|
|
498
|
+
* Marks stop re-querying while it is false; see MarkHandle.active.
|
|
499
|
+
*/
|
|
500
|
+
readonly visible: boolean;
|
|
501
|
+
/**
|
|
502
|
+
* Whether a visible chart other than this one draws `scale`: a catch-up
|
|
503
|
+
* springs such a scale rather than snapping it. See
|
|
504
|
+
* ChartVisibility.shownElsewhere.
|
|
505
|
+
*/
|
|
506
|
+
shownElsewhere(scale: object): boolean;
|
|
507
|
+
/** Whether the pointer is over any of the chart's cells (hover intent). */
|
|
508
|
+
readonly hovered: boolean;
|
|
375
509
|
readonly yScaleState: ScaleStateInstance | null | undefined;
|
|
376
510
|
readonly scaleTypes: {
|
|
377
511
|
x: string;
|
package/package.json
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"type": "git",
|
|
19
19
|
"url": "git+https://github.com/dlyngman/fjader.git"
|
|
20
20
|
},
|
|
21
|
-
"version": "0.2.
|
|
21
|
+
"version": "0.2.5",
|
|
22
22
|
"type": "module",
|
|
23
23
|
"svelte": "./dist/index.js",
|
|
24
24
|
"exports": {
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"prepack": "svelte-package",
|
|
42
42
|
"prepare": "svelte-kit sync || echo ''",
|
|
43
43
|
"format": "prettier --write .",
|
|
44
|
-
"lint": "prettier --check . && eslint .",
|
|
44
|
+
"lint": "prettier --check . && eslint . && node scripts/check-doc-blocks.mjs",
|
|
45
45
|
"test": "vitest",
|
|
46
46
|
"typecheck": "svelte-check --tsconfig ./jsconfig.json",
|
|
47
47
|
"build:widget": "vite build --config vite.widget.config.js",
|