Files
temetro/frontend/components/charts/series-bar-layout.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
1.6 KiB
TypeScript

export function computeSeriesBarWidth(input: {
innerWidth: number;
dataLength: number;
columnWidth: number;
seriesCount: number;
composedBarSize?: number;
composedMaxBarSize?: number;
composedBarGap?: number;
stacked?: boolean;
}): number {
const {
innerWidth,
dataLength,
columnWidth,
seriesCount,
composedBarSize,
composedMaxBarSize,
composedBarGap = 4,
stacked = false,
} = input;
const gap = composedBarGap;
const groupCount = stacked ? 1 : Math.max(1, seriesCount);
let slot = columnWidth;
if (slot <= 0) {
slot = dataLength < 2 ? innerWidth : innerWidth / (dataLength - 1);
}
let width =
composedBarSize ??
Math.min(slot * 0.88, composedMaxBarSize ?? Number.POSITIVE_INFINITY);
if (composedMaxBarSize != null) {
width = Math.min(width, composedMaxBarSize);
}
if (groupCount > 1) {
const maxGroup = slot * 0.92;
const needed = groupCount * width + (groupCount - 1) * gap;
if (needed > maxGroup && maxGroup > 0) {
width = Math.max(4, (maxGroup - (groupCount - 1) * gap) / groupCount);
}
}
return Math.max(2, width);
}
/** Half-width of the bar group at each x — used to pad reveal clips. */
export function computeSeriesBarRevealClipPadding(input: {
barWidth: number;
seriesCount: number;
gap?: number;
stacked?: boolean;
}): number {
const { barWidth, seriesCount, gap = 4, stacked = false } = input;
if (stacked || seriesCount <= 1) {
return Math.ceil(barWidth / 2);
}
const groupWidth = seriesCount * barWidth + (seriesCount - 1) * gap;
return Math.ceil(groupWidth / 2);
}