mirror of
https://github.com/temetro/temetro.git
synced 2026-08-11 02:57:55 +00:00
321a6298a4
- 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>
114 lines
3.0 KiB
TypeScript
114 lines
3.0 KiB
TypeScript
import {
|
||
type FadeEdges,
|
||
fadeGradientStops,
|
||
resolveFadeSides,
|
||
viewportFadeGradientAttrs,
|
||
} from "./fade-edges";
|
||
|
||
interface AreaGradientDefsProps {
|
||
gradientId: string;
|
||
strokeGradientId: string;
|
||
edgeMaskId: string;
|
||
edgeGradientId: string;
|
||
fill: string;
|
||
fillOpacity: number;
|
||
gradientToOpacity: number;
|
||
/** 0–1: where the bottom stop sits (1 = full-height gradient). */
|
||
gradientSpan?: number;
|
||
resolvedStroke: string;
|
||
isPatternFill: boolean;
|
||
fadeEdges: FadeEdges;
|
||
innerWidth: number;
|
||
innerHeight: number;
|
||
}
|
||
|
||
export function AreaGradientDefs({
|
||
gradientId,
|
||
strokeGradientId,
|
||
edgeMaskId,
|
||
edgeGradientId,
|
||
fill,
|
||
fillOpacity,
|
||
gradientToOpacity,
|
||
gradientSpan = 1,
|
||
resolvedStroke,
|
||
isPatternFill,
|
||
fadeEdges,
|
||
innerWidth,
|
||
innerHeight,
|
||
}: AreaGradientDefsProps) {
|
||
const sides = resolveFadeSides(fadeEdges);
|
||
// Stroke gradient mirrors the area's edge fade so the line doesn't pop in
|
||
// past the faded fill. Skip emitting it when neither edge fades — the line
|
||
// can then paint a solid stroke instead of an unnecessary url(#...) ref.
|
||
const strokeStops = sides.any ? fadeGradientStops(sides) : null;
|
||
const showEdgeMask = sides.any && !isPatternFill;
|
||
const edgeStops = showEdgeMask ? fadeGradientStops(sides) : null;
|
||
const span = Math.min(1, Math.max(0.01, gradientSpan));
|
||
const midOffset = `${span * 100}%`;
|
||
|
||
return (
|
||
<defs>
|
||
{isPatternFill ? null : (
|
||
<linearGradient id={gradientId} x1="0%" x2="0%" y1="0%" y2="100%">
|
||
<stop
|
||
offset="0%"
|
||
style={{ stopColor: fill, stopOpacity: fillOpacity }}
|
||
/>
|
||
<stop
|
||
offset={midOffset}
|
||
style={{ stopColor: fill, stopOpacity: gradientToOpacity }}
|
||
/>
|
||
{span < 1 ? (
|
||
<stop
|
||
offset="100%"
|
||
style={{ stopColor: fill, stopOpacity: gradientToOpacity }}
|
||
/>
|
||
) : null}
|
||
</linearGradient>
|
||
)}
|
||
|
||
{strokeStops ? (
|
||
<linearGradient
|
||
id={strokeGradientId}
|
||
{...viewportFadeGradientAttrs(innerWidth)}
|
||
>
|
||
{strokeStops.map((stop) => (
|
||
<stop
|
||
key={stop.offset}
|
||
offset={stop.offset}
|
||
style={{ stopColor: resolvedStroke, stopOpacity: stop.opacity }}
|
||
/>
|
||
))}
|
||
</linearGradient>
|
||
) : null}
|
||
|
||
{edgeStops ? (
|
||
<>
|
||
<linearGradient
|
||
id={edgeGradientId}
|
||
{...viewportFadeGradientAttrs(innerWidth)}
|
||
>
|
||
{edgeStops.map((stop) => (
|
||
<stop
|
||
key={stop.offset}
|
||
offset={stop.offset}
|
||
style={{ stopColor: "white", stopOpacity: stop.opacity }}
|
||
/>
|
||
))}
|
||
</linearGradient>
|
||
<mask id={edgeMaskId}>
|
||
<rect
|
||
fill={`url(#${edgeGradientId})`}
|
||
height={innerHeight}
|
||
width={innerWidth}
|
||
x="0"
|
||
y="0"
|
||
/>
|
||
</mask>
|
||
</>
|
||
) : null}
|
||
</defs>
|
||
);
|
||
}
|