final pan fix

This commit is contained in:
Chris Amow 2026-08-16 23:25:36 -05:00
parent dfe5224e35
commit fc09da7053
3 changed files with 64 additions and 10 deletions

View file

@ -1106,6 +1106,12 @@ that object replaced the whole option, so `pressedMouseMove` and
`axisPressedMouseMove` never returned. Desktop vertical click-drag died `axisPressedMouseMove` never returned. Desktop vertical click-drag died
after any tool use. Restore the full mouse-on, touch-off set. after any tool use. Restore the full mouse-on, touch-off set.
LWC reads `axisPressedMouseMove.price`, not a boolean. Passing `true`
made `.price` undefined and axis/price drag stayed dead. Use
`{ time: true, price: true }`. Pane vertical drag is also a no-op while
the scale is still auto; a mostly-vertical mouse drag now sets
`manualPriceRange` the same way two-finger pan does.
### 2026-08-16 — the rest of the same session, none of it subtle ### 2026-08-16 — the rest of the same session, none of it subtle
Written because they were skipped the first time and then asked for. Written because they were skipped the first time and then asked for.

View file

@ -234,7 +234,7 @@ createApp({
const dataUpdatedAt = computed(() => { const dataUpdatedAt = computed(() => {
if (dataReceivedAt.value == null) return '—'; if (dataReceivedAt.value == null) return '—';
return new Intl.DateTimeFormat(undefined, { return new Intl.DateTimeFormat(undefined, {
hour: 'numeric', minute: '2-digit', second: '2-digit', hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit',
}).format(new Date(dataReceivedAt.value)); }).format(new Date(dataReceivedAt.value));
}); });
const build = ref({ commit: '', startedAt: '' }); const build = ref({ commit: '', startedAt: '' });
@ -244,7 +244,7 @@ createApp({
const at = new Date(build.value.startedAt); const at = new Date(build.value.startedAt);
if (Number.isNaN(at.getTime())) return commit; if (Number.isNaN(at.getTime())) return commit;
return `${commit} ${new Intl.DateTimeFormat(undefined, { return `${commit} ${new Intl.DateTimeFormat(undefined, {
day: 'numeric', month: 'short', hour: 'numeric', minute: '2-digit', day: 'numeric', month: 'short', hour12: false, hour: '2-digit', minute: '2-digit',
}).format(at)}`; }).format(at)}`;
}); });
const quoteChange = computed(() => { const quoteChange = computed(() => {
@ -302,7 +302,7 @@ createApp({
function addEvent(message, kind = 'info', url = null, number = null) { function addEvent(message, kind = 'info', url = null, number = null) {
events.value = [ events.value = [
{ key: ++eventSequence, at: new Date().toLocaleTimeString(), message, kind, url, number }, { key: ++eventSequence, at: new Date().toLocaleTimeString(undefined, { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' }), message, kind, url, number },
...events.value, ...events.value,
].slice(0, 20); ].slice(0, 20);
} }

View file

@ -192,8 +192,8 @@ class ConfluenceChart {
handleScale: { handleScale: {
mouseWheel: true, mouseWheel: true,
pinch: false, pinch: false,
axisPressedMouseMove: true, axisPressedMouseMove: { time: true, price: true },
axisDoubleClickReset: true, axisDoubleClickReset: { time: true, price: true },
}, },
handleScroll: { handleScroll: {
mouseWheel: true, mouseWheel: true,
@ -750,7 +750,8 @@ class ConfluenceChart {
if (event.touches.length === 1 && this.touchTap) { if (event.touches.length === 1 && this.touchTap) {
const touch = event.touches[0]; const touch = event.touches[0];
if (Math.hypot(touch.clientX - this.touchTap.x, touch.clientY - this.touchTap.y) > 10) { if (Math.hypot(touch.clientX - this.touchTap.x, touch.clientY - this.touchTap.y) > 10) {
this.touchTap = null; this.touchTap = null;
this.mousePan = null;
} }
} }
if (!this.touchTwo || event.touches.length !== 2) return; if (!this.touchTwo || event.touches.length !== 2) return;
@ -1245,8 +1246,8 @@ class ConfluenceChart {
handleScale: tool ? false : { handleScale: tool ? false : {
mouseWheel: true, mouseWheel: true,
pinch: false, pinch: false,
axisPressedMouseMove: true, axisPressedMouseMove: { time: true, price: true },
axisDoubleClickReset: true, axisDoubleClickReset: { time: true, price: true },
}, },
}); });
this.hideSnapDot(); this.hideSnapDot();
@ -1372,9 +1373,13 @@ class ConfluenceChart {
} }
startToolGesture(event) { startToolGesture(event) {
if (!this.armedTool || event.button !== 0) return; if (event.button !== 0) return;
const point = this.eventPoint(event); const point = this.eventPoint(event);
if (!point) return; if (!point) return;
if (!this.armedTool) {
if (this.withinPlot(point)) this.mousePan = { start: point };
return;
}
event.preventDefault(); event.preventDefault();
this.gesture = { start: point, end: point }; this.gesture = { start: point, end: point };
this.chartEl.setPointerCapture?.(event.pointerId); this.chartEl.setPointerCapture?.(event.pointerId);
@ -1382,7 +1387,10 @@ class ConfluenceChart {
} }
moveToolGesture(event) { moveToolGesture(event) {
if (!this.armedTool) return; if (!this.armedTool) {
this.moveMousePan(event);
return;
}
const point = this.eventPoint(event); const point = this.eventPoint(event);
if (!point) return; if (!point) return;
// The listener is on window, so it fires over the sidebar and the price // The listener is on window, so it fires over the sidebar and the price
@ -1497,7 +1505,47 @@ class ConfluenceChart {
if (a.t != null && b.t != null) this.setLinePreview(a, b, '#65b7cf', 2); if (a.t != null && b.t != null) this.setLinePreview(a, b, '#65b7cf', 2);
} }
moveMousePan(event) {
if (!this.mousePan) return;
const point = this.eventPoint(event);
if (!point) return;
const gesture = this.mousePan;
if (gesture.minValue == null) {
const dx = point.x - gesture.start.x;
const dy = point.y - gesture.start.y;
if (Math.hypot(dx, dy) < 8) return;
if (Math.abs(dy) <= Math.abs(dx)) {
this.mousePan = null;
return;
}
const plot = this.plotCanvas()?.getBoundingClientRect();
if (!plot) {
this.mousePan = null;
return;
}
const margins = this.chart.priceScale('right').options().scaleMargins;
const top = this.candles.coordinateToPrice(plot.height * margins.top);
const bottom = this.candles.coordinateToPrice(plot.height * (1 - margins.bottom));
if (top == null || bottom == null) {
this.mousePan = null;
return;
}
gesture.minValue = Math.min(top, bottom);
gesture.maxValue = Math.max(top, bottom);
gesture.pricePerPx = (gesture.maxValue - gesture.minValue)
/ Math.max(plot.height * (1 - margins.top - margins.bottom), 1);
}
const dPrice = (point.y - gesture.start.y) * gesture.pricePerPx;
this.manualPriceRange = {
minValue: gesture.minValue + dPrice,
maxValue: gesture.maxValue + dPrice,
};
this.candles.applyOptions({ autoscaleInfoProvider: this.candleAutoscaleProvider });
this.updateCurrentPrice(this.lastCurrentPrice);
}
finishToolGesture(event) { finishToolGesture(event) {
this.mousePan = null;
if (!this.armedTool || !this.gesture) return; if (!this.armedTool || !this.gesture) return;
const tool = this.armedTool; const tool = this.armedTool;
const { start, end } = this.gesture; const { start, end } = this.gesture;