Alert frame
A frame in the alert color around content, which blinks while an alarm is unacknowledged.
On this page
Code
import {
AlertFrame,
AlertFrameTextSize,
AlertType,
ObcAlertFrameMode,
ObcAlertFrameThickness,
ObcAlertFrameType,
Readout,
ReadoutSize,
} from "@bosunkit/instruments";
import { Anchor, BatteryLow, Droplets, Thermometer, Waves } from "lucide-react";
import { useEffect, useState, type CSSProperties, type Dispatch, type ReactElement, type SetStateAction } from "react";
export const description: string =
"Eight alerts on one watch screen, one per alert type, across every frame layout, both thicknesses, both text " +
"sizes and all three lifecycle modes, with flap icons, long labels that wrap and then shorten at a word, and timers " +
"that count up live.";
const GRID: CSSProperties = {
display: "grid",
gridTemplateColumns: "repeat(auto-fit, minmax(240px, 1fr))",
columnGap: 48,
rowGap: 8,
alignItems: "start",
width: "100%",
maxWidth: 640,
padding: "0 40px 0 8px",
boxSizing: "border-box",
};
/**
* Every frame's cell keeps room above it for a top flap on two lines and below it for a bottom flap on two lines of the
* large text, so the frames of a row share their top edge whichever flap they carry, and every row is spaced alike.
*/
const CELL: CSSProperties = { paddingTop: 40, paddingBottom: 56 };
const CONTENT: CSSProperties = { padding: "10px 14px", display: "flex", flexDirection: "column", gap: 4 };
const ICON_SIZE: number = 20;
/** Minutes and seconds since the alert was raised, as the flap's timer shows them. */
function elapsed(seconds: number): string {
const minutes: number = Math.floor(seconds / 60);
const rest: number = seconds % 60;
return `${String(minutes).padStart(2, "0")}:${String(rest).padStart(2, "0")}`;
}
export default function Showcase(): ReactElement {
const [tick, setTick]: [number, Dispatch<SetStateAction<number>>] = useState<number>(0);
useEffect((): (() => void) => {
const interval: number = window.setInterval((): void => {
setTick((previous: number): number => previous + 1);
}, 1000);
return (): void => {
window.clearInterval(interval);
};
}, []);
return (
<div style={GRID}>
<div style={CELL}>
<AlertFrame
type={ObcAlertFrameType.TopFlip}
status={AlertType.Alarm}
mode={ObcAlertFrameMode.unackedActive}
thickness={ObcAlertFrameThickness.Large}
showIcon
icon={<Waves size={ICON_SIZE} />}
label={<span>Depth 2.1 m, below the 3.0 m limit. Slow down and steer for deeper water.</span>}
timer={elapsed(42 + tick)}
wrapContent
fullWidth
>
<div style={CONTENT}>
<Readout
label="Depth"
value={2.1}
fractionDigits={1}
unit="m"
src="Echosounder"
size={ReadoutSize.medium}
/>
</div>
</AlertFrame>
</div>
<div style={CELL}>
<AlertFrame
type={ObcAlertFrameType.LargeSideFlip}
status={AlertType.LevelCritical}
mode={ObcAlertFrameMode.unackedActive}
showIcon
icon={<Anchor size={ICON_SIZE} />}
wrapContent
fullWidth
>
<div style={CONTENT}>
<Readout label="From anchor" value={58} unit="m" src="GPS, swing circle 45 m" size={ReadoutSize.medium} />
</div>
</AlertFrame>
</div>
<div style={CELL}>
<AlertFrame
type={ObcAlertFrameType.BottomFlip}
status={AlertType.Warning}
mode={ObcAlertFrameMode.ackedActive}
textSize={AlertFrameTextSize.Large}
showIcon
icon={<Thermometer size={ICON_SIZE} />}
label={<span>Engine coolant 92 °C and rising. Reduce to 1800 rpm.</span>}
timer={elapsed(195 + tick)}
wrapContent
fullWidth
>
<div style={CONTENT}>
<Readout label="Coolant" value={92} unit="°C" src="Yanmar 4JH80" size={ReadoutSize.medium} />
</div>
</AlertFrame>
</div>
<div style={CELL}>
<AlertFrame
type={ObcAlertFrameType.SmallSideFlip}
status={AlertType.LevelHigh}
mode={ObcAlertFrameMode.ackedActive}
thickness={ObcAlertFrameThickness.Large}
wrapContent
fullWidth
>
<div style={CONTENT}>
<Readout label="CPA, MV Nordic Star" value={0.3} fractionDigits={1} unit="NM" src="AIS" />
</div>
</AlertFrame>
</div>
<div style={CELL}>
<AlertFrame
type={ObcAlertFrameType.SmallSideFlip}
status={AlertType.Caution}
mode={ObcAlertFrameMode.unackedActive}
wrapContent
fullWidth
>
<div style={CONTENT}>
<Readout label="Fuel tank" value={18} unit="%" src="Tank sender, about 40 L left" />
</div>
</AlertFrame>
</div>
<div style={CELL}>
<AlertFrame
type={ObcAlertFrameType.TopFlip}
status={AlertType.LevelMedium}
mode={ObcAlertFrameMode.unackedRectified}
showIcon
icon={<Droplets size={ICON_SIZE} />}
label={<span>Bilge pump ran 6 times in the last hour. The pump is dry now; check the stern gland.</span>}
timer={elapsed(1260 + tick)}
wrapContent
fullWidth
>
<div style={CONTENT}>
<Readout label="Bilge pump cycles" value={6} unit="/h" src="Bilge switch" />
</div>
</AlertFrame>
</div>
<div style={CELL}>
<AlertFrame
type={ObcAlertFrameType.BottomFlip}
status={AlertType.LevelLow}
mode={ObcAlertFrameMode.unackedActive}
showIcon
icon={<BatteryLow size={ICON_SIZE} />}
label={<span>House bank at 11.9 V. Start the engine or plug in shore power before nightfall.</span>}
timer={elapsed(600 + tick)}
wrapContent
fullWidth
>
<div style={CONTENT}>
<Readout label="House bank" value={11.9} fractionDigits={1} unit="V" src="Victron BMV" />
</div>
</AlertFrame>
</div>
<div style={CELL}>
<AlertFrame
type={ObcAlertFrameType.Regular}
status={AlertType.LevelDiagnostic}
mode={ObcAlertFrameMode.ackedActive}
sharpEdgeTopLeft
sharpEdgeBottomRight
wrapContent
fullWidth
>
<div style={CONTENT}>
<Readout label="Wind speed" value={null} unit="kn" src="Masthead sensor silent for 4 min" />
</div>
</AlertFrame>
</div>
</div>
);
}
The alert frame puts an outline in the color of an alert around the content it belongs to. mode follows the alert
lifecycle: steady while it is acknowledged and active, blinking in step with the other alerts while an alarm or an
emergency is unacknowledged, and dashed once the cause has cleared. A warning and a caution stay steady. A flap with the category icon, a label and a timer can sit on
one side, along the top or along the bottom. It ports obc-alert-frame; the overview lists the
other instruments.
Examples
Unacknowledged alarm
Code
import { AlertFrame, AlertType, ObcAlertFrameMode, Readout } from "@bosunkit/instruments";
import type { ReactElement } from "react";
export const description: string =
"An anchor drag alarm nobody has acknowledged, with the frame outline around its readout blinking in step with " +
"the other alerts on screen.";
export default function UnacknowledgedAlarm(): ReactElement {
return (
<AlertFrame
status={AlertType.Alarm}
mode={ObcAlertFrameMode.unackedActive}
wrapContent
showAlertCategoryIcon={false}
>
<div style={{ padding: "6px 12px" }}>
<Readout label="Drop distance" value={42} unit="m" src="GPS" />
</div>
</AlertFrame>
);
}
Frame the affected content
Wrap the reading or equipment region the alert actually concerns. A frame around an entire report or screen can imply that every value shares the alert. Keep the cause, priority, source and state readable inside or next to the framed region.
Supply mode from the alert lifecycle. A dashed rectified outline does not mean that an outstanding
acknowledgment has completed. The optional timer needs a stated meaning, such as time since occurrence; it is
not automatically a response deadline. Keep acknowledgment and audio control in their dedicated actions.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| type | ObcAlertFrameType | ObcAlertFrameType.SmallSideFlip | Frame layout: regular is the outline alone; the other layouts add a flap with the category icon on a small or
large side (small-side-flip, large-side-flip), along the bottom or along the top. Default small-side-flip. |
| thickness | ObcAlertFrameThickness | ObcAlertFrameThickness.Small | Border thickness: small (default) or large for more emphasis. |
| status | AlertType | AlertType.Alarm | The alert type, which selects the frame color and the category icon. Default alarm. |
| mode | ObcAlertFrameMode | ObcAlertFrameMode.ackedActive | Where the alert is in its lifecycle: acked-active (steady, default), unacked-active (blinks with the alert
blink cadence) or unacked-rectified (dashed outline). |
| textSize | AlertFrameTextSize | AlertFrameTextSize.Regular | Size of the flap's label and timer text: regular (default) or large. |
| showIcon | boolean | false | Shows icon in the flap of the large-side, bottom and top layouts. Default false. |
| showAlertCategoryIcon | boolean | true | Shows the category icon for status in the flap. Default true. |
| wrapContent | boolean | false | When true, the frame wraps and sizes itself to its slotted content instead of overlaying a fixed region. Default: false (absolute, inset 0). |
| fullWidth | boolean | false | When true, the frame stretches to the full width of its container. |
| sharpEdgeTopLeft | boolean | false | If true, the top-left corner will be sharp (not rounded). |
| sharpEdgeTopRight | boolean | false | If true, the top-right corner will be sharp (not rounded). |
| sharpEdgeBottomLeft | boolean | false | If true, the bottom-left corner will be sharp (not rounded). |
| sharpEdgeBottomRight | boolean | false | If true, the bottom-right corner will be sharp (not rounded). |
| icon | ReactNode | – | Custom icon displayed in the flap (large-side-flip, bottom-flip, top-flip). |
| label | ReactNode | – | Label text shown in the bottom/top flap. |
| timer | ReactNode | – | Timer or time label in the bottom/top flap. |
| children | ReactNode | – | Main content inside the alert frame. |
| className | string | – | Class names added to the frame's root element. |
Related
- Alert icon: the icon for the same states.
- Alarms and severity: the priority colors and shapes the alert instruments draw.