Section divider
A named rule between groups of alert cards, colored by urgency.
On this page
Code
import { AlertCard } from "@bosunkit/components/alert-card";
import { Button } from "@bosunkit/components/button";
import type { Priority } from "@bosunkit/components/priority";
import { SectionDivider, type SectionDividerVariant } from "@bosunkit/components/section-divider";
import { Check } from "lucide-react";
import { useState, type Dispatch, type ReactElement, type SetStateAction } from "react";
export const description: string =
"An alert list in its three sections, each label carrying its count: acknowledging an immediate alarm moves it " +
"under monitoring, and the cleared section recedes at the bottom.";
interface Alert {
readonly id: string;
readonly priority: Priority;
readonly title: string;
readonly time: string;
readonly message: string;
readonly section: SectionDividerVariant;
}
const ALERTS: readonly Alert[] = [
{
id: "drag",
priority: "alarm",
title: "Anchor drag",
time: "02:11",
message: "42 m from the drop position, outside the 35 m swing radius.",
section: "immediate",
},
{
id: "cpa",
priority: "alarm",
title: "Close quarters: MARIA K",
time: "02:12",
message: "Cargo vessel, closest approach 0.3 nm in 9 min. Call her on VHF 16 or alter course to starboard.",
section: "immediate",
},
{
id: "wind",
priority: "warning",
title: "Wind above the anchoring limit",
time: "01:40",
message: "24 kn from the southwest, gusting 31 kn.",
section: "monitoring",
},
{
id: "battery",
priority: "caution",
title: "House bank at 48%",
time: "00:52",
message: "Reaches 40% at about 05:30 at the present draw.",
section: "monitoring",
},
{
id: "shallow",
priority: "caution",
title: "Shallow water",
time: "23:18",
message: "Depth fell to 3.1 m on the approach; above 5.0 m since 23:26.",
section: "cleared",
},
];
const SECTIONS: readonly { readonly variant: SectionDividerVariant; readonly name: string }[] = [
{ variant: "immediate", name: "Immediate" },
{ variant: "monitoring", name: "Monitoring" },
{ variant: "cleared", name: "Cleared tonight" },
];
export default function Showcase(): ReactElement {
const [alerts, setAlerts]: [readonly Alert[], Dispatch<SetStateAction<readonly Alert[]>>] =
useState<readonly Alert[]>(ALERTS);
function acknowledge(id: string): void {
setAlerts((current: readonly Alert[]): readonly Alert[] =>
current.map((alert: Alert): Alert => (alert.id === id ? { ...alert, section: "monitoring" } : alert)),
);
}
return (
<div style={{ width: "100%", maxWidth: 560, display: "flex", flexDirection: "column", gap: 10 }}>
{SECTIONS.map((section: { readonly variant: SectionDividerVariant; readonly name: string }): ReactElement => {
const inSection: readonly Alert[] = alerts.filter((alert: Alert): boolean => alert.section === section.variant);
return (
<div key={section.variant} style={{ display: "flex", flexDirection: "column", gap: 10 }}>
<SectionDivider label={`${section.name} (${String(inSection.length)})`} variant={section.variant} />
{section.variant === "immediate" && inSection.length === 0 && (
<span style={{ fontSize: 12, color: "var(--text-dim)", textAlign: "center" }}>
Nothing needs the crew now.
</span>
)}
{inSection.map((alert: Alert): ReactElement => (
<AlertCard
key={alert.id}
priority={alert.priority}
title={alert.title}
time={alert.time}
message={alert.message}
immediate={alert.section === "immediate"}
acknowledged={alert.section !== "immediate"}
cleared={alert.section === "cleared"}
actions={
alert.section === "immediate" ? (
<Button
variant="primary"
size="sm"
onClick={(): void => {
acknowledge(alert.id);
}}
>
<Check size={12} aria-hidden="true" />
Acknowledge
</Button>
) : undefined
}
/>
))}
</div>
);
})}
</div>
);
}
A section divider names a group of alert cards with a label between two rules. Its variant sets the urgency of the
group. immediate draws the label and its rules in the alarm color, monitoring in the dim text color, and cleared
in the dim color at half strength, so the groups recede as the urgency does. In an alert card
grid the divider spans both columns. The divider is a separator.
Examples
Immediate
Code
import { AlertCard } from "@bosunkit/components/alert-card";
import { SectionDivider } from "@bosunkit/components/section-divider";
import type { ReactElement } from "react";
export const description: string = "The alarm color, over the alarm that needs the crew now.";
export default function Immediate(): ReactElement {
return (
<div style={{ width: "100%", display: "flex", flexDirection: "column", gap: 10 }}>
<SectionDivider label="Immediate" variant="immediate" />
<AlertCard
priority="alarm"
title="Anchor drag"
time="02:11"
message="42 m from the drop position, outside the 35 m swing radius."
immediate
/>
</div>
);
}
Monitoring
Code
import { AlertCard } from "@bosunkit/components/alert-card";
import { SectionDivider } from "@bosunkit/components/section-divider";
import type { ReactElement } from "react";
export const description: string = "The dim color, over the acknowledged alerts the crew is watching.";
export default function Monitoring(): ReactElement {
return (
<div style={{ width: "100%", display: "flex", flexDirection: "column", gap: 10 }}>
<SectionDivider label="Monitoring" variant="monitoring" />
<AlertCard
priority="warning"
title="Wind above the anchoring limit"
time="01:40"
message="24 kn from the southwest, gusting 31 kn."
acknowledged
/>
<AlertCard
priority="caution"
title="House bank at 48%"
time="00:52"
message="Reaches 40% at about 05:30 at the present draw."
acknowledged
/>
</div>
);
}
Cleared
Code
import { AlertCard } from "@bosunkit/components/alert-card";
import { SectionDivider } from "@bosunkit/components/section-divider";
import type { ReactElement } from "react";
export const description: string = "The dimmest rule, over the alerts that are over.";
export default function Cleared(): ReactElement {
return (
<div style={{ width: "100%", display: "flex", flexDirection: "column", gap: 10 }}>
<SectionDivider label="Cleared" variant="cleared" />
<AlertCard
priority="caution"
title="Shallow water"
time="23:18"
message="Depth fell to 3.1 m on the approach; above 5.0 m since 23:26."
acknowledged
cleared
/>
</div>
);
}
Motion
In a moving alert list (alert card grid) a divider fades in and out with its section
and slides when the cards above it change, unless the crew is using the list. Outside a list it moves only when asked,
and motion={false} stops its motion anywhere. Under reduced motion it takes its new place at once.
Code
import { AlertCard } from "@bosunkit/components/alert-card";
import { Button } from "@bosunkit/components/button";
import { AlertCardGrid } from "@bosunkit/components/alert-card-grid";
import { SectionDivider } from "@bosunkit/components/section-divider";
import { useState, type Dispatch, type ReactElement, type SetStateAction } from "react";
export const description: string =
"In an alert list the Monitoring divider slides down to make room when an alarm arrives above it, and back up " +
"when the alarm clears.";
export default function Motion(): ReactElement {
const [raised, setRaised]: [boolean, Dispatch<SetStateAction<boolean>>] = useState<boolean>(false);
return (
<div style={{ display: "flex", flexDirection: "column", gap: "var(--space-md)", width: "100%" }}>
<Button
size="sm"
onClick={(): void => {
setRaised((current: boolean): boolean => !current);
}}
>
{raised ? "The anchor holds again" : "The anchor drags"}
</Button>
<AlertCardGrid>
{raised && (
<AlertCard
key="drag"
className="alarm--full"
priority="alarm"
title="Anchor drag"
message="38 m outside the swing circle"
/>
)}
<SectionDivider key="monitoring" label="Monitoring" variant="monitoring" />
<AlertCard key="battery" priority="caution" title="House battery low" message="12.1 V, 48 % charge" />
<AlertCard key="fuel" priority="caution" title="Fuel below a quarter" message="46 L in the tank" />
</AlertCardGrid>
</div>
);
}
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| labelrequired | string | – | The section's name. |
| variantrequired | SectionDividerVariant | – | The section's urgency, which sets its color. |
| className | string | undefined | – | Classes added to the divider's own. |
| motion | MotionProp | undefined | – | How the divider moves. Inside a moving alert list (AlertCardGrid) it fades in and out with its section and slides
when the cards above it change; elsewhere it moves only when asked. false turns it off. |
Related
- Alert card grid: the grid whose columns a divider spans.
- Alert card: the cards a divider groups.
- Alarms and severity: the urgency each variant names.