@pond-ts/charts 0.62.0 → 0.64.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/API.md +1 -1
- package/CHANGELOG.md +223 -1
- package/dist/BarChart.js +6 -6
- package/dist/ChartContainer.d.ts +72 -0
- package/dist/ChartContainer.js +151 -24
- package/dist/ChartRow.d.ts +39 -3
- package/dist/ChartRow.js +194 -7
- package/dist/Layers.js +25 -8
- package/dist/XAxis.d.ts +6 -0
- package/dist/XAxis.js +81 -3
- package/dist/YAxis.d.ts +92 -2
- package/dist/YAxis.js +268 -6
- package/dist/annotations.js +14 -7
- package/dist/bars.d.ts +21 -8
- package/dist/bars.js +38 -12
- package/dist/chip.d.ts +50 -7
- package/dist/chip.js +66 -9
- package/dist/context.d.ts +134 -7
- package/dist/cursors.js +33 -8
- package/dist/domain.d.ts +18 -0
- package/dist/domain.js +33 -0
- package/dist/select.d.ts +1 -1
- package/dist/select.js +7 -2
- package/dist/tradingTimeScale.d.ts +11 -1
- package/dist/tradingTimeScale.js +53 -7
- package/dist/use-axis-gestures.d.ts +79 -0
- package/dist/use-axis-gestures.js +257 -0
- package/package.json +3 -3
package/dist/Layers.js
CHANGED
|
@@ -207,7 +207,7 @@ export function Layers({ children }) {
|
|
|
207
207
|
}), [row.registerLayer, row.unregisterLayer]);
|
|
208
208
|
const background = container.theme.background;
|
|
209
209
|
const { grid: gridColor, gridDash } = container.theme.axis;
|
|
210
|
-
const { layers, yScales, formats, defaultAxisId, tickValues, tickCounts, axisSides, } = row;
|
|
210
|
+
const { layers, yScales, baseYScales, formats, defaultAxisId, tickValues, tickCounts, axisSides, axisOffsets, axisColors, } = row;
|
|
211
211
|
// x geometry is shared and lives on the container (uniform across rows), and
|
|
212
212
|
// so is the x tick count — vertical gridlines must sit under the `<XAxis>`
|
|
213
213
|
// labels, which pass the same `xTickCount` to the same scale.
|
|
@@ -346,21 +346,23 @@ export function Layers({ children }) {
|
|
|
346
346
|
const report = container.reportDrawStats;
|
|
347
347
|
if (report === undefined) {
|
|
348
348
|
for (const entry of layers) {
|
|
349
|
-
const
|
|
349
|
+
const axisId = entry.axisId ?? defaultAxisId;
|
|
350
|
+
const yScale = yScales.get(axisId);
|
|
350
351
|
if (yScale === undefined)
|
|
351
352
|
continue;
|
|
352
|
-
entry.layer.draw(ctx, xScale, yScale);
|
|
353
|
+
entry.layer.draw(ctx, xScale, yScale, baseYScales.get(axisId));
|
|
353
354
|
}
|
|
354
355
|
}
|
|
355
356
|
else {
|
|
356
357
|
const infos = [];
|
|
357
358
|
let totalDrawMs = 0;
|
|
358
359
|
for (const entry of layers) {
|
|
359
|
-
const
|
|
360
|
+
const axisId = entry.axisId ?? defaultAxisId;
|
|
361
|
+
const yScale = yScales.get(axisId);
|
|
360
362
|
if (yScale === undefined)
|
|
361
363
|
continue;
|
|
362
364
|
const t0 = performance.now();
|
|
363
|
-
const stats = entry.layer.draw(ctx, xScale, yScale);
|
|
365
|
+
const stats = entry.layer.draw(ctx, xScale, yScale, baseYScales.get(axisId));
|
|
364
366
|
const drawMs = performance.now() - t0;
|
|
365
367
|
totalDrawMs += drawMs;
|
|
366
368
|
infos.push({
|
|
@@ -377,6 +379,7 @@ export function Layers({ children }) {
|
|
|
377
379
|
}, [
|
|
378
380
|
layers,
|
|
379
381
|
yScales,
|
|
382
|
+
baseYScales,
|
|
380
383
|
xScale,
|
|
381
384
|
xTickCount,
|
|
382
385
|
defaultAxisId,
|
|
@@ -516,14 +519,20 @@ export function Layers({ children }) {
|
|
|
516
519
|
// The chip uses this layer's axis formatter, so a readout value reads
|
|
517
520
|
// exactly as the axis labels it.
|
|
518
521
|
const fmt = formats.get(axisId) ?? String;
|
|
519
|
-
//
|
|
522
|
+
// Where the crosshair value pill lands, and in what ink: this axis's own
|
|
523
|
+
// side, its offset out into that gutter (so the pill sits on the axis that
|
|
524
|
+
// measured the value, not the innermost one), and its `<YAxis color>`.
|
|
520
525
|
const side = axisSides.get(axisId) ?? 'left';
|
|
526
|
+
const axisOffset = axisOffsets.get(axisId) ?? 0;
|
|
527
|
+
const axisColor = axisColors.get(axisId);
|
|
521
528
|
for (const s of entry.layer.sampleAt(cursorTime)) {
|
|
522
529
|
out.push({
|
|
523
530
|
px: xScale(s.x),
|
|
524
531
|
py: yScale(s.value),
|
|
525
532
|
axisId,
|
|
526
533
|
side,
|
|
534
|
+
axisOffset,
|
|
535
|
+
axisColor,
|
|
527
536
|
formatted: fmt(s.value),
|
|
528
537
|
color: s.color,
|
|
529
538
|
label: s.label,
|
|
@@ -538,6 +547,8 @@ export function Layers({ children }) {
|
|
|
538
547
|
yScales,
|
|
539
548
|
formats,
|
|
540
549
|
axisSides,
|
|
550
|
+
axisOffsets,
|
|
551
|
+
axisColors,
|
|
541
552
|
xScale,
|
|
542
553
|
defaultAxisId,
|
|
543
554
|
]);
|
|
@@ -1124,7 +1135,7 @@ export function Layers({ children }) {
|
|
|
1124
1135
|
// canvas repaints only on a mark transition — not every move (the move just
|
|
1125
1136
|
// slides the SVG cursor). A row with no selectable layer (line/area/band)
|
|
1126
1137
|
// resolves to null → a no-op. Uses the raw pointer, not the snapped x.
|
|
1127
|
-
const hit = resolveSelection(r.layers, rawX, py, c.xScale, (axisId) => r.yScales.get(axisId ?? r.defaultAxisId));
|
|
1138
|
+
const hit = resolveSelection(r.layers, rawX, py, c.xScale, (axisId) => r.yScales.get(axisId ?? r.defaultAxisId), 'hover', (axisId) => r.baseYScales.get(axisId ?? r.defaultAxisId));
|
|
1128
1139
|
// The resting BLOCK preview (a mounted <MultiSelector>): hover lights
|
|
1129
1140
|
// every mark in the snap block under the pointer — exactly the set a
|
|
1130
1141
|
// drag begun and released here would select, from exactly the sweep's
|
|
@@ -1384,7 +1395,7 @@ export function Layers({ children }) {
|
|
|
1384
1395
|
// 'select', not 'hover': a click must be able to resolve to NO mark —
|
|
1385
1396
|
// that null is the deselect signal (the empty commit) — so layers whose
|
|
1386
1397
|
// hover target is generous (a bar's full-height slot) narrow it here.
|
|
1387
|
-
const hit = resolveSelection(r.layers, e.clientX - rect.left, e.clientY - rect.top, c.xScale, (axisId) => r.yScales.get(axisId ?? r.defaultAxisId), 'select');
|
|
1398
|
+
const hit = resolveSelection(r.layers, e.clientX - rect.left, e.clientY - rect.top, c.xScale, (axisId) => r.yScales.get(axisId ?? r.defaultAxisId), 'select', (axisId) => r.baseYScales.get(axisId ?? r.defaultAxisId));
|
|
1388
1399
|
const modifiers = {
|
|
1389
1400
|
additive: e.metaKey || e.ctrlKey,
|
|
1390
1401
|
ctrlKey: e.ctrlKey,
|
|
@@ -1539,6 +1550,10 @@ export function Layers({ children }) {
|
|
|
1539
1550
|
py: cursor.cursorY,
|
|
1540
1551
|
formatted: fmt(ys.invert(cursor.cursorY)),
|
|
1541
1552
|
side: axisSides.get(defaultAxisId) ?? 'left',
|
|
1553
|
+
// The free reticle reads the row's *default* axis, so its pill belongs on
|
|
1554
|
+
// that axis — at its offset, in its ink — like a snapped sample's.
|
|
1555
|
+
axisOffset: axisOffsets.get(defaultAxisId) ?? 0,
|
|
1556
|
+
axisColor: axisColors.get(defaultAxisId),
|
|
1542
1557
|
};
|
|
1543
1558
|
}, [
|
|
1544
1559
|
wantsPointer,
|
|
@@ -1548,6 +1563,8 @@ export function Layers({ children }) {
|
|
|
1548
1563
|
yScales,
|
|
1549
1564
|
formats,
|
|
1550
1565
|
axisSides,
|
|
1566
|
+
axisOffsets,
|
|
1567
|
+
axisColors,
|
|
1551
1568
|
defaultAxisId,
|
|
1552
1569
|
]);
|
|
1553
1570
|
// The in-plot cursor time, readout-formatted — the `showTime` presets' chip
|
package/dist/XAxis.d.ts
CHANGED
|
@@ -133,6 +133,12 @@ export interface XAxisProps {
|
|
|
133
133
|
* numbers, with no axis-type prop here; the kind follows the data.
|
|
134
134
|
*
|
|
135
135
|
* `<TimeAxis>` is the time-flavoured preset (`<XAxis />`).
|
|
136
|
+
*
|
|
137
|
+
* **Gestures.** With `<ChartContainer axisPanZoom="x">` (or `"xy"`) the strip is a
|
|
138
|
+
* second handle on the canvas gesture: it **pans on drag** and **zooms on
|
|
139
|
+
* wheel**, with double-click returning to the declared `range`. Same maths as the
|
|
140
|
+
* plot's own drag, including `bounds` / `minDuration` and the trading calendar.
|
|
141
|
+
* A category axis has no continuous domain and stays inert.
|
|
136
142
|
*/
|
|
137
143
|
export declare function XAxis({ format, label, side, height, ticks: customTicks, transform, color, align, dateStyle, onMouseEvent, }?: XAxisProps): import("react/jsx-runtime").JSX.Element;
|
|
138
144
|
export {};
|
package/dist/XAxis.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Fragment, useContext } from 'react';
|
|
2
|
+
import { Fragment, useContext, useRef } from 'react';
|
|
3
3
|
import { scaleLinear } from 'd3-scale';
|
|
4
4
|
import { derivedTicks } from './derivedTicks.js';
|
|
5
5
|
import { ContainerContext, CursorContext, } from './context.js';
|
|
@@ -8,6 +8,8 @@ import { xAxisCursorEntries } from './cursors.js';
|
|
|
8
8
|
import { axisPillStyle } from './chip.js';
|
|
9
9
|
import { resolveAxisFormat, resolveTimeFormat, } from './format.js';
|
|
10
10
|
import { axisMouseProps, axisPointerPx, } from './axis-events.js';
|
|
11
|
+
import { useAxisGestures } from './use-axis-gestures.js';
|
|
12
|
+
import { panRange, panRangeTrading, zoomRange, zoomRangeTrading, } from './viewport.js';
|
|
11
13
|
/** Tick strip height (mark + value label) in CSS px. */
|
|
12
14
|
const TICK_STRIP = 22;
|
|
13
15
|
/** Extra height reserved for an axis `label` line. */
|
|
@@ -157,6 +159,12 @@ export function thinCategoryLabels(ticks, slot, plotWidth, fontSize, fontFamily)
|
|
|
157
159
|
* numbers, with no axis-type prop here; the kind follows the data.
|
|
158
160
|
*
|
|
159
161
|
* `<TimeAxis>` is the time-flavoured preset (`<XAxis />`).
|
|
162
|
+
*
|
|
163
|
+
* **Gestures.** With `<ChartContainer axisPanZoom="x">` (or `"xy"`) the strip is a
|
|
164
|
+
* second handle on the canvas gesture: it **pans on drag** and **zooms on
|
|
165
|
+
* wheel**, with double-click returning to the declared `range`. Same maths as the
|
|
166
|
+
* plot's own drag, including `bounds` / `minDuration` and the trading calendar.
|
|
167
|
+
* A category axis has no continuous domain and stays inert.
|
|
160
168
|
*/
|
|
161
169
|
export function XAxis({ format, label, side = 'bottom', height, ticks: customTicks, transform, color, align = 'center', dateStyle = 'flat', onMouseEvent, } = {}) {
|
|
162
170
|
const container = useContext(ContainerContext);
|
|
@@ -417,14 +425,84 @@ export function XAxis({ format, label, side = 'bottom', height, ticks: customTic
|
|
|
417
425
|
// scale — no gutter arithmetic. The label reads the same channel a cursor
|
|
418
426
|
// pill does: the band scale's category name on a category axis (a d3 number
|
|
419
427
|
// format can't name one), this axis's readout format everywhere else.
|
|
428
|
+
// The view the current pan started from — see `onDragStart`.
|
|
429
|
+
const panStartRef = useRef(null);
|
|
430
|
+
// Drag pans and wheel zooms the shared x view, double-click returns to the
|
|
431
|
+
// declared range. Enabled by the container's own `panZoom` zoom-x degree of
|
|
432
|
+
// freedom — a chart that never opted in captures nothing here.
|
|
433
|
+
//
|
|
434
|
+
// A **category** axis has no continuous domain to zoom, exactly as for the
|
|
435
|
+
// plot's gesture (`Layers`' wheel makes the same exclusion), so the strip
|
|
436
|
+
// stays inert there rather than snapping between slots.
|
|
437
|
+
const gestures = useAxisGestures({
|
|
438
|
+
axis: 'x',
|
|
439
|
+
// Gated on `<ChartContainer axisPanZoom>` — the axis opt-in — and NOT on the
|
|
440
|
+
// plot's `panZoom`: inheriting that would hand every already-interactive
|
|
441
|
+
// chart gestures its author never asked for. Once opted in the strip is the
|
|
442
|
+
// canvas gesture (drag pans, wheel zooms). A category axis has no continuous
|
|
443
|
+
// domain for either.
|
|
444
|
+
drag: container.axisPanZoomX && xKind !== 'category' ? 'pan' : 'none',
|
|
445
|
+
wheel: container.axisPanZoomX && xKind !== 'category',
|
|
446
|
+
// Snapshot the view at press: the pan is re-derived from it on every move
|
|
447
|
+
// (the plot's own approach), so a long drag can't accumulate rounding, and
|
|
448
|
+
// `roundRange`'s ms snap can't ratchet the span.
|
|
449
|
+
onDragStart: () => {
|
|
450
|
+
panStartRef.current = [container.timeRange[0], container.timeRange[1]];
|
|
451
|
+
},
|
|
452
|
+
onPan: (totalDeltaPx) => {
|
|
453
|
+
const start = panStartRef.current;
|
|
454
|
+
if (start === null)
|
|
455
|
+
return;
|
|
456
|
+
// Dragging right moves the view EARLIER — the content follows the pointer,
|
|
457
|
+
// the sign the plot's drag uses.
|
|
458
|
+
if (container.discontinuities) {
|
|
459
|
+
// Trading-time axis: pan by an equal amount of *trading* time so the
|
|
460
|
+
// drag feels uniform across collapsed gaps (a raw-ms shift jumps).
|
|
461
|
+
const fraction = plotWidth > 0 ? -totalDeltaPx / plotWidth : 0;
|
|
462
|
+
container.applyRange(panRangeTrading(start, fraction, container.discontinuities));
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
const span = start[1] - start[0];
|
|
466
|
+
const dt = plotWidth > 0 ? -totalDeltaPx * (span / plotWidth) : 0;
|
|
467
|
+
container.applyRange(panRange(start, dt, { log: container.xIsLog, snap: xKind === 'time' }));
|
|
468
|
+
},
|
|
469
|
+
onZoom: (factor, pivotPx) => {
|
|
470
|
+
const pivot = +xScale.invert(pivotPx);
|
|
471
|
+
// The same two-branch zoom the plot uses, so an axis drag and a plot
|
|
472
|
+
// wheel move the view by identical maths — including `minDuration` as the
|
|
473
|
+
// zoom-in floor and, on a trading axis, a floor in *trading* ms.
|
|
474
|
+
// `applyRange` then applies `bounds` for both.
|
|
475
|
+
container.applyRange(container.discontinuities
|
|
476
|
+
? zoomRangeTrading(container.timeRange, pivot, factor, container.discontinuities, container.minDuration)
|
|
477
|
+
: zoomRange(container.timeRange, pivot, factor, container.minDuration, {
|
|
478
|
+
log: container.xIsLog,
|
|
479
|
+
snap: xKind === 'time',
|
|
480
|
+
}));
|
|
481
|
+
},
|
|
482
|
+
onReset: () => container.applyRange(container.seedRange),
|
|
483
|
+
});
|
|
420
484
|
const mouse = axisMouseProps(onMouseEvent, 'x', undefined, (event) => {
|
|
485
|
+
// A zoom drag ends with a trailing `click` on the strip, which would report
|
|
486
|
+
// as "clicked the axis at the value I released on" — a value the user never
|
|
487
|
+
// aimed at. Swallow exactly that one report; every other event still flows.
|
|
488
|
+
if (event.type === 'click' && gestures.consumeDrag())
|
|
489
|
+
return null;
|
|
421
490
|
const value = +xScale.invert(axisPointerPx(event, 'x', [0, plotWidth]));
|
|
422
491
|
return {
|
|
423
492
|
value,
|
|
424
493
|
label: xKind === 'category' ? fmt(value) : readoutFmt(value),
|
|
425
494
|
};
|
|
426
495
|
});
|
|
427
|
-
return (_jsxs("div", { "data-axis": "x", ...mouse,
|
|
496
|
+
return (_jsxs("div", { "data-axis": "x", ref: gestures.ref, ...gestures.props, ...mouse,
|
|
497
|
+
// Both spreads carry an `onDoubleClick` — the gesture's reset and the
|
|
498
|
+
// consumer's report — and a spread silently keeps the last one. Compose
|
|
499
|
+
// them, reporting *before* the reset so the payload describes the view the
|
|
500
|
+
// user actually double-clicked in.
|
|
501
|
+
onDoubleClick: (e) => {
|
|
502
|
+
mouse.onDoubleClick?.(e);
|
|
503
|
+
gestures.props.onDoubleClick?.();
|
|
504
|
+
}, style: {
|
|
505
|
+
...gestures.style,
|
|
428
506
|
position: 'relative',
|
|
429
507
|
marginLeft: `${leftGutter}px`,
|
|
430
508
|
width: `${plotWidth}px`,
|
|
@@ -514,7 +592,7 @@ export function XAxis({ format, label, side = 'bottom', height, ticks: customTic
|
|
|
514
592
|
fontWeight: 600,
|
|
515
593
|
whiteSpace: 'nowrap',
|
|
516
594
|
overflow: 'hidden',
|
|
517
|
-
}, children: b.label }, b.start));
|
|
595
|
+
}, children: b.showLabel ? b.label : '' }, b.start));
|
|
518
596
|
}) })), label !== undefined && (_jsx("div", { style: {
|
|
519
597
|
position: 'absolute',
|
|
520
598
|
left: 0,
|
package/dist/YAxis.d.ts
CHANGED
|
@@ -195,7 +195,14 @@ export interface YAxisProps {
|
|
|
195
195
|
* overriding the theme's `axis.label` / `axis.title.color`. The multi-axis
|
|
196
196
|
* convention of colouring each y axis to match its series (`color`
|
|
197
197
|
* matching the layer's) — busy, but standard. Omit for the theme's axis
|
|
198
|
-
* colours.
|
|
198
|
+
* colours.
|
|
199
|
+
*
|
|
200
|
+
* **Also worn by the axis-edge chrome that lands on this axis** — a
|
|
201
|
+
* `<CrosshairCursor>`'s value pill takes it when the reticle reads a series
|
|
202
|
+
* scaled here, so with several axes the pill says *which* scale the number is
|
|
203
|
+
* on (the ChartIQ price-tag convention). That is why it rides on the
|
|
204
|
+
* registered spec: the pill is drawn by the row's cursor overlay, not by this
|
|
205
|
+
* component, so a colour it never registered could not reach it.
|
|
199
206
|
*/
|
|
200
207
|
color?: string;
|
|
201
208
|
/**
|
|
@@ -209,8 +216,83 @@ export interface YAxisProps {
|
|
|
209
216
|
* menu, down/up, move, enter, leave — so switch on `event.type`. Nothing is
|
|
210
217
|
* attached when the prop is omitted, so the move events cost nothing unless
|
|
211
218
|
* you ask for them. A `hide`den axis draws no gutter and so fires nothing.
|
|
219
|
+
*
|
|
220
|
+
* A gutter that also zooms (see the component docs) still reports every event
|
|
221
|
+
* here, minus the trailing `click` a zoom drag would otherwise synthesize.
|
|
212
222
|
*/
|
|
213
223
|
onMouseEvent?: AxisMouseHandler;
|
|
224
|
+
/**
|
|
225
|
+
* A gutter gesture scaled this axis — **the "auto vs manual" hand-off.**
|
|
226
|
+
* Fires with the `[min, max]` **bounds** the gesture arrived at, and with
|
|
227
|
+
* `null` when the axis is released back to auto-fit (double-click).
|
|
228
|
+
*
|
|
229
|
+
* Named for bounds rather than the domain because that is what it reports: with
|
|
230
|
+
* a `pad` set, the visible domain is these bounds *plus* the padding, and it is
|
|
231
|
+
* the bounds you hand back as `min`/`max`.
|
|
232
|
+
*
|
|
233
|
+
* The common shape this exists for: an auto-fitting y axis on a chart whose x
|
|
234
|
+
* is panned and zoomed. The moment the user scrolls or drags the y gutter they
|
|
235
|
+
* have overridden the fit, and a UI usually wants to *say* so — show the
|
|
236
|
+
* resulting min/max, mark the scale "manual", and offer a toggle back to auto
|
|
237
|
+
* (which is the same thing double-clicking the gutter does).
|
|
238
|
+
*
|
|
239
|
+
* ```tsx
|
|
240
|
+
* const [scale, setScale] = useState<[number, number] | null>(null); // null = auto
|
|
241
|
+
* <YAxis
|
|
242
|
+
* id="price"
|
|
243
|
+
* {...(scale ? { min: scale[0], max: scale[1] } : {})}
|
|
244
|
+
* onBoundsChange={setScale}
|
|
245
|
+
* />
|
|
246
|
+
* ```
|
|
247
|
+
*
|
|
248
|
+
* **Providing it makes the axis controlled**, exactly as `onTimeRangeChange`
|
|
249
|
+
* does for the x view: the gesture then only *reports*, and what the axis draws
|
|
250
|
+
* is whatever `min`/`max` you feed back. Omit it and the axis holds the zoom
|
|
251
|
+
* itself (an internal per-axis transform) — which is the standalone behaviour,
|
|
252
|
+
* and why a chart with no scale UI needs no wiring at all.
|
|
253
|
+
*
|
|
254
|
+
* The reported pair is in data units, ready to hand straight back as
|
|
255
|
+
* `min`/`max`.
|
|
256
|
+
*
|
|
257
|
+
* **`scale="symlog"` is approximate on this path, by construction.**
|
|
258
|
+
* {@link linearWindow} is a fraction of the *domain*, so bounds fed back
|
|
259
|
+
* re-derive the knee and reshape the curve — the grabbed pixel cannot be held
|
|
260
|
+
* on a curve that moves with the bounds. (It is the same fact that makes
|
|
261
|
+
* `linearWindow` deliberately *not* recompute under a 2-D gesture.) The zoom is
|
|
262
|
+
* still monotone and well-behaved; if you need the pixel held exactly on a
|
|
263
|
+
* symlog axis, leave this callback off and let the axis hold the zoom itself,
|
|
264
|
+
* where the knee stays anchored to the resolved domain. With an active plot-level y zoom (`panZoom="panZoomY"`/`"panZoomXY"`)
|
|
265
|
+
* the two **compose**: the bounds are the axis's own, and the plot transform
|
|
266
|
+
* still narrows what is drawn on top of them. On a `log` axis it stays positive (the zoom is done in log
|
|
267
|
+
* space), so it is always a domain the axis can actually draw.
|
|
268
|
+
*/
|
|
269
|
+
onBoundsChange?: (bounds: readonly [number, number] | null) => void;
|
|
270
|
+
/**
|
|
271
|
+
* Pin the zoom to the value-**0** gridline instead of the pointer, and drop
|
|
272
|
+
* drag-to-pan entirely — for a bar chart, whose baseline must never move.
|
|
273
|
+
* A bar rests on `0` (or, straddling positive and negative, has `0`
|
|
274
|
+
* *somewhere inside* the visible range) — panning or zooming about an
|
|
275
|
+
* arbitrary pointer position would slide that baseline around the plot,
|
|
276
|
+
* which reads as the data moving under gestures that never touched it (see
|
|
277
|
+
* `resolveBarBaseline`, and [PND-XBASE] for the still-open x-axis
|
|
278
|
+
* counterpart of this exact failure mode). `zeroAnchored` sidesteps it:
|
|
279
|
+
* every wheel notch scales the axis around wherever `0` currently renders,
|
|
280
|
+
* so the baseline holds however far in or out you scroll — whether it sits
|
|
281
|
+
* at the plot floor (all-positive bars) or in the middle (bars that
|
|
282
|
+
* straddle zero).
|
|
283
|
+
*
|
|
284
|
+
* One qualification: the pivot is clamped into the axis's own pixel range,
|
|
285
|
+
* so if `0` has been scrolled off the plot entirely the zoom pivots about
|
|
286
|
+
* the nearer edge instead and the baseline does creep. For a bar chart that
|
|
287
|
+
* is benign — the clamp lands on the same floor `resolveBarBaseline`
|
|
288
|
+
* already resolves against — but it is a creep, not a guarantee.
|
|
289
|
+
*
|
|
290
|
+
* Still gated by the container's own `axisPanZoom` opt-in (`'y'` / `'xy'`)
|
|
291
|
+
* — this only changes *what* the gesture does once enabled, not whether
|
|
292
|
+
* it's enabled. Double-click still resets to the declared/auto-fit view.
|
|
293
|
+
* Default `false`.
|
|
294
|
+
*/
|
|
295
|
+
zeroAnchored?: boolean;
|
|
214
296
|
/**
|
|
215
297
|
* @internal Declaration position among the row's children, injected by
|
|
216
298
|
* `ChartRow` so the first-declared axis stays the default. Do not set.
|
|
@@ -224,6 +306,14 @@ export interface YAxisProps {
|
|
|
224
306
|
* computes this axis's scale from the charts linked to it; the gutter then draws
|
|
225
307
|
* tick marks + labels from that scale. Charts attach via `<LineChart axis="id">`
|
|
226
308
|
* (default: the first axis).
|
|
309
|
+
*
|
|
310
|
+
* **Gestures.** With `<ChartContainer axisPanZoom="y">` (or `"xy"`) the gutter is
|
|
311
|
+
* grabbable: drag or wheel it to scale **this axis only**
|
|
312
|
+
* — a sibling axis on the other side, and every other row, hold still — and
|
|
313
|
+
* double-click to release it back to its fit. That per-axis scaling is what the
|
|
314
|
+
* plot's vertical gesture deliberately cannot do; see
|
|
315
|
+
* {@link RowFrame.axisTransforms}. Report it to a scale UI with
|
|
316
|
+
* {@link YAxisProps.onBoundsChange}.
|
|
227
317
|
*/
|
|
228
|
-
export declare function YAxis({ id, side, label, scale, linearWindow, min, max, format, ticks, tickCount, pad, boundaryLabels, width, hide, labelPlacement, color, onMouseEvent, index, }: YAxisProps): import("react/jsx-runtime").JSX.Element | null;
|
|
318
|
+
export declare function YAxis({ id, side, label, scale, linearWindow, min, max, format, ticks, tickCount, pad, boundaryLabels, width, hide, labelPlacement, color, onMouseEvent, onBoundsChange, zeroAnchored, index, }: YAxisProps): import("react/jsx-runtime").JSX.Element | null;
|
|
229
319
|
//# sourceMappingURL=YAxis.d.ts.map
|