Files
temetro/frontend/components/charts/use-highlight-segment.ts
T
Khalid Abdi 321a6298a4 frontend: pharmacy inventory, lab add-result & analysis charts
- Pharmacy: the sidebar entry now expands into Pharmacy + a new Inventory page
  (searchable medication stock with derived in-stock/low/out availability,
  backed by /api/inventory). Fix the dispensing-queue "Expiring" badge so it
  only flags courses ending within the next 7 days, not ones already elapsed.
- Lab "Add analysis result": the patient picker no longer lists patients until
  you type and supports arrow-key + Enter selection; the test field offers a
  catalog of common analyses with an Advanced option (custom analysis + ref
  range); submitted results now show immediately in a Recent results feed.
- Analysis: add a Live panel (real-time line chart) and replace the flat trend
  sparklines with bklit-ui area + bar charts (installed from the @bklit shadcn
  registry; chart CSS variables wired into the theme for light and dark).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 19:22:07 +03:00

62 lines
2.0 KiB
TypeScript

"use client";
import { useSpring } from "motion/react";
import { useMemo, useRef } from "react";
import { useChartConfig } from "./chart-config-context";
import { useChartHover, useChartStable } from "./chart-context";
import {
computeSegmentBounds,
INACTIVE_SEGMENT,
} from "./highlight-segment-bounds";
// Hover-highlight band for `line.tsx` and `area.tsx`. Computes the segment
// bounds and springs its x/width; `<HighlightSegment>` renders the clipped
// re-stroke. Spring tuning comes from `ChartConfigProvider.highlightSpring`.
// Stable + hover slices are read separately so callers can see the exact
// subscription surface (anything calling this hook will re-render on hover).
export interface HighlightSegmentResult {
xSpring: ReturnType<typeof useSpring>;
widthSpring: ReturnType<typeof useSpring>;
isActive: boolean;
}
/**
* @param enabled set false when there is no stroke to highlight (e.g. an area
* with `showLine={false}`); defaults true.
*/
export function useHighlightSegment({
enabled = true,
}: {
enabled?: boolean;
} = {}): HighlightSegmentResult {
const { data, xScale, xAccessor } = useChartStable();
const { tooltipData, selection } = useChartHover();
const { highlightSpring } = useChartConfig();
const bounds = useMemo(
() =>
enabled
? computeSegmentBounds(data, xScale, xAccessor, tooltipData, selection)
: INACTIVE_SEGMENT,
[enabled, data, xScale, xAccessor, tooltipData, selection]
);
const xSpring = useSpring(0, highlightSpring);
const widthSpring = useSpring(0, highlightSpring);
// Jump on inactive→active so the band appears at the hovered point instead
// of sliding in from x=0; ease on subsequent moves.
const wasActive = useRef(false);
if (bounds.isActive && !wasActive.current) {
xSpring.jump(bounds.x);
widthSpring.jump(bounds.width);
} else {
xSpring.set(bounds.x);
widthSpring.set(bounds.width);
}
wasActive.current = bounds.isActive;
return { xSpring, widthSpring, isActive: bounds.isActive };
}