Bearing input
A bearing or a direction typed as degrees or as a point of the compass, stepped round the circle and drawn on a small rose.
On this page
Code
import { BearingInput } from "@bosunkit/components/bearing-input";
import { useState, type Dispatch, type ReactElement, type SetStateAction } from "react";
export const description: string =
"A log entry's directions: the wind from the southwest, the tidal stream setting northeast, and the course steered " +
"in degrees magnetic. Each takes degrees or a point of the compass, steps round the circle with the arrow keys and " +
"draws its bearing on the rose before it.";
export default function Showcase(): ReactElement {
const [wind, setWind]: [number | null, Dispatch<SetStateAction<number | null>>] = useState<number | null>(225);
const [set, setSet]: [number | null, Dispatch<SetStateAction<number | null>>] = useState<number | null>(45);
const [course, setCourse]: [number | null, Dispatch<SetStateAction<number | null>>] = useState<number | null>(358);
return (
<div style={{ width: "100%", display: "flex", flexWrap: "wrap", gap: "var(--space-lg)" }}>
<BearingInput label="Wind direction" sense="from" value={wind} onValueChange={setWind} />
<BearingInput label="Tidal stream set" sense="to" value={set} onValueChange={setSet} />
<BearingInput label="Course steered" reference="magnetic" value={course} onValueChange={setCourse} />
</div>
);
}
A bearing input holds a direction in whole degrees from 000 to 359: a course, a bearing to a mark, the direction a wind or a swell comes from or a current goes to. A number field gets these wrong in three ways. It stores 360 beside 000 for north, stops at 359 where the circle turns on to 000, and writes 045 as 45. This field reads degrees, with or without a degree sign, rounded to the whole degree. A T or M after them must name the field's own north: a bearing from the other north differs by the variation, so the field refuses it and says to correct it for variation, never taking 045° M as 045° T. It also reads a point of the compass, which it takes as the bearing at the point's middle, so SW is 225. When the crew leave the field it writes the value in three digits with its north after it, °T or °M.
The points are read in the crew's language first and then in English. German and Dutch write O for east, French,
Spanish and Italian write O for west, and Dutch writes Z for south, so a typed O is east on a German screen and west
on a French one; the full names, Südwest or sud-ouest, are read too. The nearest of the sixteen points, each 22.5°
wide, is written under the field in words, as bosund writes a direction in a sentence. With sense it reads as where
the direction comes from or goes to, "From southwest" or "Towards northeast".
The field is a spinbutton from 0 to 359, read aloud as "225 degrees true". The arrow keys step it by one degree and by ten with Shift, Page Up and Page Down by ten, and every step turns round the circle, so 359 and one more is 000. From an empty field the first step lands on north. The rose before the value marks north and draws a needle along the bearing, so a bearing typed with its digits swapped points visibly the wrong way. A bearing past 359, a negative one or text it cannot read keeps its text and names the problem, and the field reports no bearing until it is fixed, so a form never saves the one that stood before.
Examples
Empty
Code
import { BearingInput } from "@bosunkit/components/bearing-input";
import type { ReactElement } from "react";
export const description: string = "Empty: the rose shows north and no needle, and the hint says what to enter.";
export default function Empty(): ReactElement {
return <BearingInput label="Swell direction" sense="from" hint="Degrees, or a point such as WSW" />;
}
Filled
Code
import { BearingInput } from "@bosunkit/components/bearing-input";
import type { ReactElement } from "react";
export const description: string = "Filled: three digits, the north after them, and the nearest point under the field.";
export default function Filled(): ReactElement {
return <BearingInput label="Bearing to the light" defaultValue={7} />;
}
From or towards
Code
import { BearingInput } from "@bosunkit/components/bearing-input";
import type { ReactElement } from "react";
export const description: string =
"With `sense`, the hint says which way the direction points: a wind or a swell from a point, a current or a " +
"course towards one. The value is the same bearing either way.";
export default function Sense(): ReactElement {
return (
<div style={{ display: "flex", flexWrap: "wrap", gap: "var(--space-lg)" }}>
<BearingInput label="Wind direction" sense="from" defaultValue={315} />
<BearingInput label="Current set" sense="to" defaultValue={315} />
</div>
);
}
Magnetic
Code
import { BearingInput } from "@bosunkit/components/bearing-input";
import type { ReactElement } from "react";
export const description: string =
'`reference="magnetic"` writes °M after the value and says degrees magnetic to a screen reader, for a bearing ' +
"taken with a hand-bearing compass.";
export default function Magnetic(): ReactElement {
return <BearingInput label="Hand-bearing compass" reference="magnetic" defaultValue={132} />;
}
Invalid
Code
import { BearingInput } from "@bosunkit/components/bearing-input";
import type { ReactElement } from "react";
export const description: string =
"A bearing past 359, a negative one or text the field cannot read keeps its text and says why once the crew leave " +
"the field.";
export default function Invalid(): ReactElement {
return (
<BearingInput
label="Course to steer"
defaultValue={40}
error="A bearing runs from 000 to 359 degrees; 400 is past 359. Enter a bearing in that range."
/>
);
}
Read-only
Code
import { BearingInput } from "@bosunkit/components/bearing-input";
import type { ReactElement } from "react";
export const description: string = "Read-only: the bearing shown with a dashed outline; the keys do not step it.";
export default function ReadOnly(): ReactElement {
return <BearingInput label="Leading line" readOnly defaultValue={296} />;
}
Disabled
Code
import { BearingInput } from "@bosunkit/components/bearing-input";
import type { ReactElement } from "react";
export const description: string = "Disabled: dimmed, and takes no input.";
export default function Disabled(): ReactElement {
return <BearingInput label="Wind direction" sense="from" disabled />;
}
Inside a form field
Code
import { BearingInput } from "@bosunkit/components/bearing-input";
import { FormField } from "@bosunkit/components/form-field";
import type { ReactElement } from "react";
export const description: string = "Inside a form field, named by `aria-label`.";
export default function InFormField(): ReactElement {
return (
<FormField label="Anchor bearing">
<BearingInput aria-label="Anchor bearing" defaultValue={190} />
</FormField>
);
}
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| label | string | undefined | – | The field's name on the outline, translated by the caller; omit it inside a FormField. |
| value | number | null | undefined | – | The bearing in whole degrees from 0 to 359, or null for none. |
| defaultValue | number | null | undefined | – | The bearing it starts with when the caller does not hold value. |
| onValueChange | ((degrees: number | null) => void) | undefined | – | Called with the bearing when the crew step it with the keys, or type one and leave the field; null when cleared. |
| reference | BearingReference | undefined | "true" | The north it is measured from, written after the value: true (°T) by default, or magnetic (°M). |
| sense | BearingSense | undefined | – | Whether it is where something comes from (a wind, a swell) or where it goes to (a current, a course), said under the field with the point. |
| hint | string | undefined | – | Text under the field while it holds no bearing, translated by the caller. |
| error | string | undefined | – | The caller's own error, shown in place of the field's. |
| required | boolean | undefined | – | Whether the field must hold a bearing. |
| disabled | boolean | undefined | – | Whether the field takes no input. |
| readOnly | boolean | undefined | – | Whether the field shows its bearing without letting the crew change it. |
| id | string | undefined | – | The input's id, for a Label's htmlFor. |
| name | string | undefined | – | The form field name; the form receives the whole degrees. |
| aria-label | string | undefined | – | Names the input when there is no label. |
| aria-describedby | string | undefined | – | Further ids that describe the input. |
| className | string | undefined | – | Classes added to the field's root. |
| testId | string | undefined | – | data-testid on the field. |
| ref | Ref<HTMLInputElement> | undefined | – | The input element. |
| motion | MotionProp | undefined | – | How the error line moves; off by default, as FormField's. |
Related
- Position input: a latitude and longitude, the other half of a fix.
- Compass: the instrument that shows a live heading.
- Outlined field: the box every maritime input draws in.