feat(mui): add ProgressBar + TimeField kit primitives
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
34
src/admin/ui/ProgressBar.tsx
Normal file
34
src/admin/ui/ProgressBar.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import LinearProgress from "@mui/material/LinearProgress";
|
||||
import Box from "@mui/material/Box";
|
||||
|
||||
export type ProgressColor =
|
||||
| "primary"
|
||||
| "success"
|
||||
| "warning"
|
||||
| "error"
|
||||
| "info";
|
||||
|
||||
/** Determinate linear progress bar. `value` is 0–100 (clamped). */
|
||||
export default function ProgressBar({
|
||||
value,
|
||||
color = "primary",
|
||||
height = 8,
|
||||
}: {
|
||||
value: number;
|
||||
color?: ProgressColor;
|
||||
height?: number;
|
||||
}) {
|
||||
const clamped = Number.isFinite(value)
|
||||
? Math.max(0, Math.min(100, value))
|
||||
: 0;
|
||||
return (
|
||||
<Box sx={{ width: "100%" }}>
|
||||
<LinearProgress
|
||||
variant="determinate"
|
||||
value={clamped}
|
||||
color={color}
|
||||
sx={{ height, borderRadius: height / 2 }}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
38
src/admin/ui/TimeField.tsx
Normal file
38
src/admin/ui/TimeField.tsx
Normal file
@@ -0,0 +1,38 @@
|
||||
import { TimePicker } from "@mui/x-date-pickers/TimePicker";
|
||||
import { parse, format, isValid } from "date-fns";
|
||||
|
||||
// Fixed reference date — only the time part matters for parse/format round-trips.
|
||||
const REF = new Date(2000, 0, 1);
|
||||
|
||||
/**
|
||||
* Time input over MUI X TimePicker. String in / string out ("HH:mm", or ""),
|
||||
* 24-hour, configurable minute step (default 5) — matches the legacy
|
||||
* AdminDatePicker mode="time". cs locale comes from MuiProvider's
|
||||
* LocalizationProvider.
|
||||
*/
|
||||
export default function TimeField({
|
||||
value,
|
||||
onChange,
|
||||
label,
|
||||
disabled,
|
||||
minutesStep = 5,
|
||||
}: {
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
label?: string;
|
||||
disabled?: boolean;
|
||||
minutesStep?: number;
|
||||
}) {
|
||||
const parsed = value ? parse(value, "HH:mm", REF) : null;
|
||||
return (
|
||||
<TimePicker
|
||||
label={label}
|
||||
disabled={disabled}
|
||||
ampm={false}
|
||||
minutesStep={minutesStep}
|
||||
value={parsed && isValid(parsed) ? parsed : null}
|
||||
onChange={(d) => onChange(d && isValid(d) ? format(d, "HH:mm") : "")}
|
||||
slotProps={{ textField: { size: "small", fullWidth: true } }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -17,6 +17,7 @@ export type { TabDef } from "./Tabs";
|
||||
export { default as Pagination } from "./Pagination";
|
||||
export { default as DateField } from "./DateField";
|
||||
export { default as MonthField } from "./MonthField";
|
||||
export { default as TimeField } from "./TimeField";
|
||||
export { default as PageHeader } from "./PageHeader";
|
||||
export { default as EmptyState } from "./EmptyState";
|
||||
export { default as LoadingState } from "./LoadingState";
|
||||
@@ -24,3 +25,5 @@ export { default as FilterBar } from "./FilterBar";
|
||||
export { default as StatCard } from "./StatCard";
|
||||
export type { StatCardColor } from "./StatCard";
|
||||
export { default as FileUpload } from "./FileUpload";
|
||||
export { default as ProgressBar } from "./ProgressBar";
|
||||
export type { ProgressColor } from "./ProgressBar";
|
||||
|
||||
Reference in New Issue
Block a user