Progress Button
Button with an integrated timed progress layer and completion callback.
Install
One command copies the item's source into your project, where you own and edit it.
npx manteen add @house/button-progressDependencies
@mantine/core@^9npm@mantine/hooks@^9npm
Installing this item also installs @house/mantine-ui-license from the registry.
Preview
Usage
A copy-ready example published by the registry author. Imports use the aliases Manteen configures at init.
import { ButtonProgress } from "@/components/ui/button-progress";
export function UploadFilesButton() { return ( <ButtonProgress idleLabel="Upload files" progressLabel="Uploading files" completeLabel="Files uploaded" durationMs={2500} onComplete={() => console.log("upload complete")} /> );}Props
Author-documented props, carried verbatim from the registry item.
| Prop | Type | Default | Description |
|---|---|---|---|
idleLabel | string | "Upload files" | Sets the label shown before the action has started. |
progressLabel | string | "Uploading files" | Sets the label shown while progress is animating toward completion. |
completeLabel | string | "Files uploaded" | Sets the label shown once the progress run has finished. |
durationMs | number | 2000 | Controls how long the simulated progress run takes to reach completion. |
onComplete | () => void | — | Fires once the progress bar reaches 100% and the button switches to its complete state. |
...others | Omit<ButtonProps, "children" | "onClick" | "classNames" | "styles" | "vars" | "unstyled" | "attributes" | "variant"> | — | All other props are forwarded to the root <Button>, after its own fullWidth, so both can be overridden; onClick and color are controlled by the component's internal progress/complete state and always win. Mantine Styles API props (classNames, styles, unstyled) are also accepted. |
* required
Styling
ButtonProgress exposes these parts through the public classNames/styles interface:
rootlabel
Author-declared; Manteen reports the declaration but does not verify each selector is wired.
Source
2 installable files ship with this item.
registry/mantine-ui/button-progress/button-progress.tsxcomponent · 3.3 kB · tsx
/** * Adapted from Mantine UI's ButtonProgress at * ffbf61c559f374a7ea28fcf00355e84dcbe9a908. MIT licensed; see * LICENSES/MANTINE-UI.txt after installation. */
import { Button, type ButtonProps, type ButtonVariant, type Factory, factory, Progress, rgba, type StylesApiProps, useMantineTheme, useProps, useStyles,} from "@mantine/core";import { useInterval } from "@mantine/hooks";import { useState } from "react";
import classes from "./button-progress.module.css";
export type ButtonProgressStylesNames = "root" | "label";
export interface ButtonProgressProps extends Omit< ButtonProps, | "children" | "onClick" | "classNames" | "styles" | "vars" | "unstyled" | "attributes" | "variant" >, StylesApiProps<ButtonProgressFactory> { /** Key of Button's variant, e.g. `"filled"` @default 'filled' */ variant?: ButtonVariant; idleLabel?: string; progressLabel?: string; completeLabel?: string; durationMs?: number; onComplete?: () => void;}
export type ButtonProgressFactory = Factory<{ props: ButtonProgressProps; ref: HTMLButtonElement; stylesNames: ButtonProgressStylesNames;}>;
const TICK_MS = 20;
export const ButtonProgress = factory<ButtonProgressFactory>((_props) => { const props = useProps("ButtonProgress", null, _props); const { classNames, className, style, styles, unstyled, vars, attributes, ref, idleLabel = "Upload files", progressLabel = "Uploading files", completeLabel = "Files uploaded", durationMs = 2000, onComplete, color, ...others } = props;
const theme = useMantineTheme(); const [progress, setProgress] = useState(0); const [complete, setComplete] = useState(false); const step = (TICK_MS / Math.max(durationMs, TICK_MS)) * 100;
const getStyles = useStyles<ButtonProgressFactory>({ name: "ButtonProgress", classes, props, className, style, classNames, styles, unstyled, attributes, vars, });
const interval = useInterval(() => { setProgress((current) => { const next = current + step; if (next < 100) return next;
interval.stop(); setComplete(true); onComplete?.(); return 0; }); }, TICK_MS);
const start = () => { if (complete) { setComplete(false); return; } if (!interval.active) interval.start(); };
return ( <Button ref={ref} fullWidth unstyled={unstyled} {...getStyles("root")} {...others} onClick={start} color={complete ? "teal.9" : (color ?? `${theme.primaryColor}.9`)} > <span {...getStyles("label")} aria-live="polite"> {progress > 0 ? progressLabel : complete ? completeLabel : idleLabel} </span> {progress > 0 && ( <Progress value={progress} className={classes.progress} color={rgba(theme.colors.blue[2], 0.35)} radius="sm" /> )} </Button> );});
ButtonProgress.classes = classes;ButtonProgress.displayName = "ButtonProgress";
export namespace ButtonProgress { export type Props = ButtonProgressProps; export type StylesNames = ButtonProgressStylesNames; export type Factory = ButtonProgressFactory;}registry/mantine-ui/button-progress/button-progress.module.cssstyle · 402 B · css
/* * Adapted from Mantine UI's ButtonProgress at * ffbf61c559f374a7ea28fcf00355e84dcbe9a908. MIT licensed; see * LICENSES/MANTINE-UI.txt after installation. */.root { position: relative; transition: background-color 150ms ease;}
.progress { position: absolute; z-index: 0; inset: -1px; height: auto; background-color: transparent;}
.label { position: relative; z-index: 1;}Author notes
Adapted from Mantine UI ButtonProgress at ffbf61c559…; labels, duration and completion are configurable.