Files
temetro/frontend/components/charts/indicator-fade.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

65 lines
1.5 KiB
TypeScript

export type IndicatorFadeEdges = "both" | "none" | "top" | "bottom";
export interface VerticalFadeSides {
top: boolean;
bottom: boolean;
any: boolean;
}
export function resolveVerticalFadeSides(
fade: IndicatorFadeEdges | boolean
): VerticalFadeSides {
if (fade === false || fade === "none") {
return { top: false, bottom: false, any: false };
}
if (fade === true || fade === "both") {
return { top: true, bottom: true, any: true };
}
if (fade === "top") {
return { top: true, bottom: false, any: true };
}
return { top: false, bottom: true, any: true };
}
export interface IndicatorFadeGradientStop {
offset: string;
opacity: number;
}
/** Opacity stops for the crosshair vertical gradient. */
export function indicatorFadeGradientStops(
sides: VerticalFadeSides,
fadeLengthPercent = 10
): IndicatorFadeGradientStop[] {
const fade = Math.min(40, Math.max(2, fadeLengthPercent));
const innerEnd = 100 - fade;
if (!sides.any) {
return [{ offset: "0%", opacity: 1 }];
}
if (sides.top && sides.bottom) {
return [
{ offset: "0%", opacity: 0 },
{ offset: `${fade}%`, opacity: 1 },
{ offset: "50%", opacity: 1 },
{ offset: `${innerEnd}%`, opacity: 1 },
{ offset: "100%", opacity: 0 },
];
}
if (sides.top) {
return [
{ offset: "0%", opacity: 0 },
{ offset: `${fade}%`, opacity: 1 },
{ offset: "100%", opacity: 1 },
];
}
return [
{ offset: "0%", opacity: 1 },
{ offset: `${innerEnd}%`, opacity: 1 },
{ offset: "100%", opacity: 0 },
];
}