@lobehub/ui
Version:
Lobe UI is an open-source UI component library for building AIGC web apps
181 lines (180 loc) • 5.61 kB
JavaScript
"use client";
import Icon from "../../Icon/Icon.mjs";
import { styles, trackHeight } from "./style.mjs";
import { memo, useMemo } from "react";
import { jsx, jsxs } from "react/jsx-runtime";
import { cssVar, cx } from "antd-style";
import { Check, X } from "lucide-react";
//#region src/base-ui/Progress/Progress.tsx
const circleDiameterMap = {
large: 72,
middle: 40,
small: 20
};
const blockHeightMap = {
large: 12,
middle: 8,
small: 4
};
const circleStrokeMap = {
large: 4,
middle: 3,
small: 2
};
const statusColor = {
active: cssVar.colorInfo,
exception: cssVar.colorError,
success: cssVar.colorSuccess
};
const Progress = memo(({ className, format, label, percent, ref, segments = 20, showInfo = true, size = "middle", status = "normal", strokeColor, style, type = "line", variant = "line", ...rest }) => {
const clamped = Math.min(100, Math.max(0, percent));
const barColor = strokeColor ?? (status === "normal" ? cssVar.colorPrimary : statusColor[status]);
const info = useMemo(() => {
if (status === "success") return /* @__PURE__ */ jsx(Icon, {
icon: Check,
size: 14,
style: { color: barColor }
});
if (status === "exception") return /* @__PURE__ */ jsx(Icon, {
icon: X,
size: 14,
style: { color: barColor }
});
return format ? format(clamped) : `${clamped}%`;
}, [
status,
format,
clamped,
barColor
]);
const progressAria = {
"aria-valuemax": 100,
"aria-valuemin": 0,
"aria-valuenow": clamped,
"aria-valuetext": useMemo(() => {
if (!format) return void 0;
const value = format(clamped);
return typeof value === "string" || typeof value === "number" ? String(value) : void 0;
}, [format, clamped]),
"role": "progressbar"
};
if (type === "circle") {
const isPreset = typeof size === "string";
const diameter = isPreset ? circleDiameterMap[size] : size;
const stroke = isPreset ? circleStrokeMap[size] : Math.max(2, Math.round(diameter * 3 / 40));
const radius = diameter / 2 - stroke;
const circumference = 2 * Math.PI * radius;
const offset = circumference * (1 - clamped / 100);
return /* @__PURE__ */ jsxs("div", {
className: cx(styles.circleRoot, className),
ref,
style,
...progressAria,
...rest,
children: [/* @__PURE__ */ jsxs("svg", {
className: styles.circleSvg,
height: diameter,
viewBox: `0 0 ${diameter} ${diameter}`,
width: diameter,
children: [/* @__PURE__ */ jsx("circle", {
className: styles.circleTrack,
cx: diameter / 2,
cy: diameter / 2,
r: radius,
strokeWidth: stroke
}), /* @__PURE__ */ jsx("circle", {
cx: diameter / 2,
cy: diameter / 2,
fill: "none",
r: radius,
stroke: barColor,
strokeDasharray: circumference,
strokeDashoffset: offset,
strokeLinecap: "round",
strokeWidth: stroke,
style: { transition: "stroke-dashoffset 0.3s" }
})]
}), showInfo && diameter >= 40 && /* @__PURE__ */ jsx("span", {
className: styles.circleInfo,
style: { fontSize: Math.round(diameter * .15 + 6) },
children: info
})]
});
}
const heightStyle = typeof size === "number" ? { height: size } : void 0;
const heightClass = (family) => typeof size === "number" ? void 0 : trackHeight({
family,
size
});
if (variant === "segments") {
const filled = Math.round(segments * clamped / 100);
return /* @__PURE__ */ jsxs("div", {
className: cx(styles.rowRoot, className),
ref,
style,
...progressAria,
...rest,
children: [/* @__PURE__ */ jsx("div", {
className: cx(styles.segments, heightClass("block")),
style: heightStyle,
children: Array.from({ length: segments }, (_, index) => /* @__PURE__ */ jsx("span", {
className: cx(styles.segment, index < filled && styles.segmentOn),
style: index < filled ? { background: barColor } : void 0
}, index))
}), showInfo && /* @__PURE__ */ jsx("span", {
className: styles.rowInfo,
children: info
})]
});
}
if (variant === "inset") return /* @__PURE__ */ jsxs("div", {
className: cx(styles.rowRoot, className),
ref,
style,
...progressAria,
...rest,
children: [/* @__PURE__ */ jsxs("div", {
className: cx(styles.split, heightClass("block")),
style: heightStyle,
children: [clamped > 0 && /* @__PURE__ */ jsx("span", {
className: cx(styles.splitFill, status === "active" && styles.barActive),
style: {
backgroundColor: barColor,
minWidth: typeof size === "number" ? size : blockHeightMap[size],
width: clamped === 100 ? "100%" : `calc((100% - 3px) * ${clamped / 100})`
}
}), clamped < 100 && /* @__PURE__ */ jsx("span", { className: styles.splitRest })]
}), showInfo && /* @__PURE__ */ jsx("span", {
className: styles.rowInfo,
children: info
})]
});
return /* @__PURE__ */ jsxs("div", {
className: cx(styles.lineRoot, className),
ref,
style,
...progressAria,
...rest,
children: [(label || showInfo) && /* @__PURE__ */ jsxs("div", {
className: styles.lineMeta,
children: [/* @__PURE__ */ jsx("span", { children: label }), showInfo && /* @__PURE__ */ jsx("span", {
className: styles.lineValue,
children: info
})]
}), /* @__PURE__ */ jsx("div", {
className: cx(styles.lineTrack, heightClass("line")),
style: heightStyle,
children: /* @__PURE__ */ jsx("div", {
style: {
backgroundColor: barColor,
width: `${clamped}%`
},
className: cx(styles.bar, variant === "line" && status === "active" && styles.barActive)
})
})]
});
});
Progress.displayName = "Progress";
//#endregion
export { Progress as default };
//# sourceMappingURL=Progress.mjs.map