67 lines
2.0 KiB
TypeScript
67 lines
2.0 KiB
TypeScript
import type { JSX } from "solid-js";
|
|
import { splitProps } from "solid-js";
|
|
|
|
export interface ProgressProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
/** Current value. Pass null for indeterminate. */
|
|
value?: number | null;
|
|
/** Maximum value. @default 100 */
|
|
max?: number;
|
|
/** Custom label function for aria-valuetext. */
|
|
getValueLabel?: (value: number, max: number) => string;
|
|
}
|
|
|
|
/**
|
|
* Displays the progress of a task. Supports determinate and indeterminate states.
|
|
* Indeterminate when value is null or undefined.
|
|
*/
|
|
export function Progress(props: ProgressProps): JSX.Element {
|
|
const [local, rest] = splitProps(props, ["value", "max", "getValueLabel"]);
|
|
|
|
const max = () => local.max ?? 100;
|
|
const isIndeterminate = () => local.value == null;
|
|
const currentValue = (): number | null => local.value ?? null;
|
|
|
|
const percentage = () => {
|
|
const v = currentValue();
|
|
if (v === null) return null;
|
|
return Math.round((v / max()) * 100);
|
|
};
|
|
|
|
const valueLabel = (): string | undefined => {
|
|
const v = currentValue();
|
|
if (v === null) return undefined;
|
|
if (local.getValueLabel) return local.getValueLabel(v, max());
|
|
const pct = percentage();
|
|
return pct === null ? undefined : `${pct}%`;
|
|
};
|
|
|
|
const dataState = (): string => {
|
|
return isIndeterminate() ? "indeterminate" : "complete";
|
|
};
|
|
|
|
const valueNow = (): number | undefined => {
|
|
const v = currentValue();
|
|
return isIndeterminate() || v === null ? undefined : v;
|
|
};
|
|
|
|
const dataValue = (): number | undefined => {
|
|
const v = currentValue();
|
|
return isIndeterminate() || v === null ? undefined : v;
|
|
};
|
|
|
|
return (
|
|
// biome-ignore lint/a11y/useFocusableInteractive: progressbar is read-only, not interactive
|
|
<div
|
|
role="progressbar"
|
|
aria-valuemin={0}
|
|
aria-valuemax={max()}
|
|
aria-valuenow={valueNow()}
|
|
aria-valuetext={valueLabel()}
|
|
data-state={dataState()}
|
|
data-value={dataValue()}
|
|
data-max={max()}
|
|
{...rest}
|
|
/>
|
|
);
|
|
}
|