forked from OurHike/OurHike
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathHeader.tsx
More file actions
87 lines (81 loc) · 2.6 KB
/
Copy pathHeader.tsx
File metadata and controls
87 lines (81 loc) · 2.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
// The map screen's header - a read-only zone (WIREFRAMES.md §2).
//
// Trail + state eyebrow, current mile + direction in mono, and on the right
// exactly two icon buttons: legend, then search. WIREFRAMES.md's words are
// "Nothing else lives here." That is worth honouring literally: this is the
// most valuable strip on the screen and the obvious place for scope to creep,
// so the component takes no children and exposes no slot.
export type HikeDirection = 'NOBO' | 'SOBO'
export interface HeaderProps {
trailName: string
state: string
/** Current position along the trail, in miles. */
mile: number
direction: HikeDirection
onOpenLegend: () => void
onOpenSearch: () => void
}
/**
* Always one decimal place, with a thousands separator: "1,407.2".
* Fixed precision keeps the number from changing width as the hiker walks,
* which would otherwise make the whole header twitch.
*/
function formatMile(mile: number): string {
return mile.toLocaleString('en-US', {
minimumFractionDigits: 1,
maximumFractionDigits: 1,
})
}
export function Header({
trailName,
state,
mile,
direction,
onOpenLegend,
onOpenSearch,
}: HeaderProps) {
return (
<header className="map-header">
<div className="map-header__read">
<p className="map-header__eyebrow">
{trailName} · {state}
</p>
<p className="map-header__position">{`mi ${formatMile(mile)} · ${direction}`}</p>
</div>
<div className="map-header__actions">
<button type="button" className="map-header__button" onClick={onOpenLegend}>
<span className="visually-hidden">Legend</span>
<svg aria-hidden="true" viewBox="0 0 24 24" width="20" height="20">
<path
d="M8 6h13M8 12h13M8 18h13M3 6h.01M3 12h.01M3 18h.01"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
/>
</svg>
</button>
<button type="button" className="map-header__button" onClick={onOpenSearch}>
<span className="visually-hidden">Search</span>
<svg aria-hidden="true" viewBox="0 0 24 24" width="20" height="20">
<circle
cx="11"
cy="11"
r="7"
fill="none"
stroke="currentColor"
strokeWidth="2"
/>
<path
d="m20 20-3.5-3.5"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
/>
</svg>
</button>
</div>
</header>
)
}