UNPKG

@lobehub/ui

Version:

Lobe UI is an open-source UI component library for building AIGC web apps

181 lines (180 loc) • 5.61 kB
"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