Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions benchmarks/conformance/cases/90-zoomable-time-window/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,31 @@ export function visibleZoomData(rows: readonly AaplRow[], window: ZoomWindow) {
})
}

export function visibleZoomDataWithNeighbors(
rows: readonly AaplRow[],
window: ZoomWindow,
) {
const start = window.start.getTime()
const end = window.end.getTime()
let firstVisible = -1
let lastVisible = -1

for (let index = 0; index < rows.length; index += 1) {
const timestamp = rows[index]!.Date.getTime()
if (timestamp < start) continue
if (timestamp > end) break
if (firstVisible < 0) firstVisible = index
Comment on lines +56 to +60

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

ast-grep outline benchmarks/conformance/cases/90-zoomable-time-window/model.ts --items all

rg -n -C 8 '\bselectZoomRows\b|\bzoomRows\b' \
  benchmarks/conformance/cases/90-zoomable-time-window

rg -n -C 6 'sort\(|Date\.getTime\(|Date' \
  packages/charts-demo-data \
  benchmarks/conformance/cases/90-zoomable-time-window

Repository: TanStack/charts

Length of output: 50372


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

# Inspect the target file around the helper and data source usages.
sed -n '1,140p' benchmarks/conformance/cases/90-zoomable-time-window/model.ts

# Locate selectZoomRows definitions/usages narrowly.
rg -n -C 5 '\bsselectZoomRows\b|selectZoomRows\(' .

# Locate aapl dataset/exports and nearby file contents.
rg -n -C 6 '(aapl|Aapl|AAPL|AaplRow)' benchmarks/conformance/cases/90-zoomable-time-window packages/charts-demo-data | head -n 240

# Behavioral probe: implement the helper loop for representative orderings.
node - <<'JS'
function selectZoomRows(rows, window) {
  const start = window.start.getTime()
  const end = window.end.getTime()
  let firstVisible = -1
  const visible = []
  for (let index = 0; index < rows.length; index += 1) {
    const timestamp = rows[index].Date.getTime()
    if (timestamp < start) continue
    if (timestamp > end) break
    if (firstVisible < 0) firstVisible = index
    visible.push(rows[index])
  }
  return visible
}
const window = { start: new Date('2007-08-24T00:00:00Z'), end: new Date('2007-08-31T00:00:00Z') }
const rows = [
  {Date: new Date('2007-08-24Z')}, {Date: new Date('2007-08-27Z')}, {Date: new Date('2007-08-28Z')},
  {Date: new Date('2007-08-25Z')}, {Date: new Date('2010-01-01Z')}
]
console.log(JSON.stringify({
  ascendingSlice: selectZoomRows(
    rows.filter((_, i, a) => i < 3),
    window
  ),
  outOfOrderSlice: selectZoomRows(rows, window),
  descendingSlice: selectZoomRows(rows.slice(0, 3).reverse(), window)
}))
JS

Repository: TanStack/charts

Length of output: 29091


Fix the neighbor scan to be order-agnostic.

visibleZoomData selects rows by timestamp, but the new visibleZoomDataWithNeighbors stops the scan after timestamp > end. That can miss out-of-order rows still inside the window and return neighbors for only a prefix of the selected rows. Update it to scan all rows from both sides/nearest rows, or keep it dependent on an explicit ascending Date invariant.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@benchmarks/conformance/cases/90-zoomable-time-window/model.ts` around lines
56 - 60, The neighbor scan in visibleZoomDataWithNeighbors must not break when a
timestamp exceeds end, because rows may be out of order. Remove the early
termination and scan every row, preserving selection of the first visible row
and collecting neighbors based on each row’s timestamp within the window;
alternatively, explicitly enforce and document an ascending Date invariant
before retaining the break.

lastVisible = index
}

if (firstVisible < 0) return []

return rows.slice(
Math.max(0, firstVisible - 1),
Math.min(rows.length, lastVisible + 2),
)
}

export function zoomSpanDays(window: ZoomWindow) {
return (window.end.getTime() - window.start.getTime()) / millisecondsPerDay
}
Expand Down
9 changes: 6 additions & 3 deletions benchmarks/conformance/cases/90-zoomable-time-window/view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
initialZoomWindow,
selectZoomRows,
visibleZoomData,
visibleZoomDataWithNeighbors,
zoomDateFromAnchor,
zoomDateKey,
zoomFullDomain,
Expand Down Expand Up @@ -69,19 +70,20 @@ const ZoomableTimeWindowExample = forwardRef<
})
const [state, setState] = useState(stateRef.current)
const [scene, setScene] = useState<ChartScene<AaplRow> | null>(null)
const rows = visibleZoomData(zoomRows, state.window)
const visibleRows = visibleZoomData(zoomRows, state.window)
const lineRows = visibleZoomDataWithNeighbors(zoomRows, state.window)
const definition = useMemo(
() =>
defineChart(
defineChart({
marks: [
lineY(rows, {
lineY(lineRows, {
x: 'Date',
y: 'Close',
stroke: color,
strokeWidth: 2.5,
}),
dot(rows, {
dot(visibleRows, {
Comment on lines +73 to +86

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

rg -n -C 8 'zoomGeometry|pointsBounds|geometry|role|visibleZoomData' \
  benchmarks/conformance

Repository: TanStack/charts

Length of output: 50372


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "## target view.tsx relevant sections"
sed -n '1,180p' benchmarks/conformance/cases/90-zoomable-time-window/view.tsx
echo
sed -n '480,560p' benchmarks/conformance/cases/90-zoomable-time-window/view.tsx

echo
echo "## local model exports"
sed -n '1,90p' benchmarks/conformance/cases/90-zoomable-time-window/model.ts

echo
echo "## focused conformance files"
sed -n '20,30p' benchmarks/conformance/cases/90-zoomable-time-window/case.json
rg -n -C 4 'zoomGeometry|pointsBounds|geometry|role' benchmarks/conformance/cases/90-zoomable-time-window/view.tsx

Repository: TanStack/charts

Length of output: 13027


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "## rest of target view.tsx"
sed -n '560,630p' benchmarks/conformance/cases/90-zoomable-time-window/view.tsx

echo
echo "## behavioral simulation of point/window/data splits"
node - <<'JS'
const zoomFullDomain = [
  new Date(Date.UTC(2018, 0, 2)),
  new Date(Date.UTC(2018, 0, 18)),
]
const row = (dayOffset) => ({
  Date: new Date(zoomFullDomain[0].getTime() + dayOffset * 86400_000),
  Close: dayOffset,
})
const zoomRows = Array.from({ length: 17 }, (_, i) => row(i))
const window = { start: zoomRows[6].Date, end: zoomRows[8].Date }
function visibleZoomData(rows, window) {
  const start = window.start.getTime()
  const end = window.end.getTime()
  return rows.filter((row) => {
    const timestamp = row.Date.getTime()
    return timestamp >= start && timestamp <= end
  })
}
function visibleZoomDataWithNeighbors(rows, window) {
  const start = window.start.getTime()
  const end = window.end.getTime()
  let firstVisible = -1
  let lastVisible = -1
  for (let index = 0; index < rows.length; index += 1) {
    const timestamp = rows[index].Date.getTime()
    if (timestamp < start) continue
    if (timestamp > end) break
    if (firstVisible < 0) firstVisible = index
    lastVisible = index
  }
  if (firstVisible < 0) return []
  return rows.slice(Math.max(0, firstVisible - 1), Math.min(rows.length, lastVisible + 2))
}
function pointsBounds(points, bounds, scaleX, scaleY, color) {
  if (points.length === 0) return null
  let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity
  const halfStroke = 1.25
  for (const [px, py] of points) {
    minX = Math.min(minX, px)
    maxX = Math.max(maxX, px)
    minY = Math.min(minY, py)
    maxY = Math.max(maxY, py)
  }
  minX = Math.max(bounds.left, minX - halfStroke)
  maxX = Math.min(bounds.left + bounds.width, maxX + halfStroke)
  minY = Math.max(bounds.top, minY - halfStroke)
  maxY = Math.min(bounds.top + bounds.height, maxY + halfStroke)
  if (maxX - minX < 0 || maxY - minY < 0) return null
  return { x: minX, y: minY, width: maxX - minX, height: maxY - minY, paint: color }
}
const modelStart = new Date('2018-01-07T00:00:00Z')
const modelEnd = new Date('2018-01-09T00:00:00Z')
const bounds = { left: 58, top: 56, width: 450, height: 340 }
const scaleX = bounds.width / bounds.width
const scaleY = bounds.height / bounds.height
const visible = visibleZoomData(zoomRows, window)
const lineData = visibleZoomDataWithNeighbors(zoomRows, window)
console.log(JSON.stringify({
  windowRange: [window.start.toISOString().slice(0,10), modelEnd.toISOString().slice(0,10)],
  visibleDataDays: visible.map(r => r.Date.toISOString().slice(0,10)),
  lineDataDays: lineData.map(r => r.Date.toISOString().slice(0,10)),
  visiblePointsCount: visible.length,
  linePointsCount: lineData.length,
  visibleBoundsIfUsed: pointsBounds(visible.map(r => [r.Date.getTime(), r.Close]), bounds, scaleX, scaleY, 'red'),
  lineBoundsIfUsed: pointsBounds(lineData.map(r => [r.Date.getTime(), r.Close]), bounds, scaleX, scaleY, 'red'),
}, null, 2))
JS

Repository: TanStack/charts

Length of output: 1545


Use the rendered line data for line geometry.

lineY renders lineRows, and those include the window neighbors needed for viewport-edge line segments. Keep visibleZoomData for role: 'dot', but build the role: 'line' bounds from visibleZoomDataWithNeighbors(zoomRows, state.window); clip the returned rectangle into the chart area to keep the geometry contract aligned with the rendered mark.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@benchmarks/conformance/cases/90-zoomable-time-window/view.tsx` around lines
73 - 86, Update the line geometry bounds in the chart definition around lineY
and visibleZoomDataWithNeighbors to use the rendered lineRows data, while
keeping visibleRows for the dot mark. Build the role: 'line' bounds from
lineRows and clip the resulting rectangle to the chart area so it remains within
the geometry contract.

x: 'Date',
y: 'Close',
fill: color,
Expand Down Expand Up @@ -109,6 +111,7 @@ const ZoomableTimeWindowExample = forwardRef<
grid: true,
axis: { ticks: { count: 4 }, label: 'AAPL close ($)' },
},
clip: true,
margin: { top: 56, right: 24, bottom: 44, left: 58 },
}),
{ animate: false, keyboard: false, focus: focusDisabled },
Expand Down