Due chip
A small chip marking a record whose date has passed or is coming, with the priority's shape as well as its color.
On this page
Service 3
Documents 3
Code
import { DueChip } from "@bosunkit/components/due-chip";
import { SectionHeading } from "@bosunkit/components/section-heading";
import { FileText, FireExtinguisher, LifeBuoy, Radio, ShieldCheck, Wrench, type LucideIcon } from "lucide-react";
import type { ReactElement } from "react";
export const description: string =
"SV Althea's documents and service schedule, with overdue items in the warning treatment, items coming due in " +
"the caution one, and the records in date left plain.";
interface DueRecord {
readonly id: string;
readonly icon: LucideIcon;
readonly name: string;
readonly detail: string;
/** The chip, or null for a record in date. */
readonly due: { readonly priority: "warning" | "caution"; readonly label: string } | null;
/** What a record in date shows. */
readonly validUntil?: string;
}
const SECTIONS: readonly { readonly title: string; readonly records: readonly DueRecord[] }[] = [
{
title: "Service",
records: [
{
id: "liferaft",
icon: LifeBuoy,
name: "Liferaft service",
detail: "Viking 6-person RescYou, last serviced in Palma; the hydrostatic release is due with it.",
due: { priority: "warning", label: "Overdue by 12 days" },
},
{
id: "impeller",
icon: Wrench,
name: "Raw-water impeller",
detail: "Yanmar 4JH57, 1 412 engine hours; replace every 500 hours or each season.",
due: { priority: "caution", label: "Due in 38 engine hours" },
},
{
id: "extinguishers",
icon: FireExtinguisher,
name: "Fire extinguishers",
detail: "Three 2 kg dry powder: saloon, galley and the cockpit locker.",
due: { priority: "caution", label: "Due in 9 days" },
},
],
},
{
title: "Documents",
records: [
{
id: "insurance",
icon: ShieldCheck,
name: "Insurance certificate",
detail: "Third-party cover for the Balearics, with the Spanish translation the port police ask for.",
due: { priority: "warning", label: "Expired 3 days ago" },
},
{
id: "vhf",
icon: Radio,
name: "Ship station license",
detail: "VHF with DSC, MMSI 224123456, call sign EA1234.",
due: { priority: "caution", label: "Expires in 3 weeks" },
},
{
id: "registration",
icon: FileText,
name: "Registration",
detail: "Spanish flag, home port Valencia.",
due: null,
validUntil: "Valid until March 2029",
},
],
},
];
export default function Showcase(): ReactElement {
return (
<div
style={{
display: "grid",
gridTemplateColumns: "repeat(auto-fit, minmax(300px, 1fr))",
gap: "var(--space-xl)",
width: "100%",
}}
>
{SECTIONS.map((section: { readonly title: string; readonly records: readonly DueRecord[] }): ReactElement => (
<section key={section.title} style={{ display: "flex", flexDirection: "column", gap: "var(--space-md)" }}>
<SectionHeading level={3} count={section.records.length}>
{section.title}
</SectionHeading>
{section.records.map((record: DueRecord): ReactElement => {
const Icon: LucideIcon = record.icon;
return (
<div key={record.id} style={{ display: "flex", gap: "var(--space-md)", alignItems: "flex-start" }}>
<Icon size={18} aria-hidden="true" style={{ flexShrink: 0, marginTop: 2, color: "var(--text-dim)" }} />
<div style={{ display: "flex", flexDirection: "column", gap: "var(--space-xs)", minWidth: 0 }}>
<span style={{ fontWeight: 600 }}>{record.name}</span>
<span style={{ fontSize: 12, color: "var(--text-dim)" }}>{record.detail}</span>
<span>
{record.due === null ? (
<span style={{ fontFamily: "var(--font-mono)", fontSize: 11, color: "var(--text-dim)" }}>
{record.validUntil}
</span>
) : (
<DueChip priority={record.due.priority} label={record.due.label} testId={`due-${record.id}`} />
)}
</span>
</div>
</div>
);
})}
</section>
))}
</div>
);
}
A due chip marks a record whose date has come or is coming, such as a document running out or a service falling due. It draws the priority's shape beside its color and never flashes: an expiry is not an alarm, so it takes the warning and caution treatments only, or none for a date not yet near. The caller supplies the label already translated. A countdown drawn as a chip is a due chip, with its timer as the label.
Examples
Warning
Code
import { DueChip } from "@bosunkit/components/due-chip";
import type { ReactElement } from "react";
export const description: string = "An obligation already past its date, in the warning color and shape.";
export default function Warning(): ReactElement {
return <DueChip priority="warning" label="Overdue" />;
}
Caution
Code
import { DueChip } from "@bosunkit/components/due-chip";
import type { ReactElement } from "react";
export const description: string = "An obligation coming due, in the caution color and shape.";
export default function Caution(): ReactElement {
return <DueChip priority="caution" label="Due in 3 days" />;
}
No treatment yet
Code
import { DueChip } from "@bosunkit/components/due-chip";
import type { ReactElement } from "react";
export const description: string = "An obligation not yet near its date, in the chip's neutral colors with no shape.";
export default function Neutral(): ReactElement {
return <DueChip priority="none" label="Due in 5 months" />;
}
Motion
By default a due chip fades its new words in when the label changes, as when a service falls due; changeKey makes it
fade when something else changes instead, as a countdown's treatment. motion takes the kit's
motion prop: true also scales it in and out inside a MotionPresence, and false turns
it off. It never flashes, whatever motion holds: an expiry is not an alarm. Under reduced motion it only fades.
Code
import { Button } from "@bosunkit/components/button";
import { DueChip } from "@bosunkit/components/due-chip";
import { useState, type Dispatch, type ReactElement, type SetStateAction } from "react";
export const description: string =
"A life raft service chip that fades its new words in by default when the date passes and it turns from caution " +
"to warning, beside the same chip with motion={false}. It never flashes: an expiry is not an alarm.";
export default function Motion(): ReactElement {
const [overdue, setOverdue]: [boolean, Dispatch<SetStateAction<boolean>>] = useState<boolean>(false);
return (
<div style={{ display: "flex", flexWrap: "wrap", alignItems: "center", gap: "var(--space-sm)" }}>
<Button
size="sm"
onClick={(): void => {
setOverdue((current: boolean): boolean => !current);
}}
>
{overdue ? "Back a day" : "A day later"}
</Button>
<span>Life raft service</span>
<DueChip priority={overdue ? "warning" : "caution"} label={overdue ? "Overdue" : "Due tomorrow"} />
<span>Without motion</span>
<DueChip
priority={overdue ? "warning" : "caution"}
label={overdue ? "Overdue" : "Due tomorrow"}
motion={false}
/>
</div>
);
}
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| priorityrequired | Extract<Priority, "warning" | "caution" | "none"> | – | Treatment: warning for an obligation already past its date, caution for one coming due, none for one not yet near (the chip's neutral colors and no shape). |
| labelrequired | ReactNode | – | Display text, already translated by the caller ("Overdue", "Due in 12 days"). It may carry markup, as a
countdown's timer and <time> element. |
| changeKey | unknown | – | What the fade on change follows. Unless set, it is the label when the label is text or a number, and the priority when it is markup, which is a new object on every render. A countdown passes its treatment, so a tick doesn't fade. |
| testId | string | undefined | – | Becomes data-testid on the root. |
| motion | MotionProp | undefined | – | How the chip moves. By default it fades its new words in when the label changes, as a service falls due. true
also scales it in as it mounts, wherever it is, and out when it leaves a MotionPresence; a preset (fade, rise,
scale) or { enter, exit, change } chooses; false turns it off. It never flashes, as an expiry is not an alarm. |
Related
- Alarms and severity: the priority shapes and colors the chip draws.
- Alert card: the alert a lapsed date can raise.