Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 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 88 89 90 91 92 93 94 95 96 97 98 99 100 | 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 3x 2x 3x 1x 1x 1x 1x 1x 1x 1x 1x 2x 2x 2x 2x 2x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 2x 2x 2x 2x 2x | /**
* The *Marks* group every picture's right-click menu carries (§8.1): hide the
* mark the hand is on, or every mark — on the map, beside it, in the 1D
* browser, in a tracks cell and on a model alike.
*
* One builder for the five menus, because they are one act over one store:
* the Manual annotations card lists the marks and every picture draws them, so
* a mark hidden from a menu is hidden from all of them and its row's eye on
* the card goes dark with it. Written once, the menus cannot come to disagree
* about its words or about what hiding means.
*
* **What the hand is on comes first**, as a feature under the pointer does on
* a lane's menu: a reader who right-clicks a rule they want gone is pointing at
* it, and sending them to find its row on the card is sending them away from
* the thing they are deciding about. Only drawn marks can be under a pointer —
* a hidden one is not there to point at — so the row is always a *hide*; the
* card's eye, `A` and the switch below bring marks back.
*/
import { describeMark, type ManualMark, type ManualStore } from '../annotations/manual'
import { KEYS, keyLabel, withKey } from './commands'
import type { MenuItem } from './Menu'
/** What one mark is called in a menu row: the card's three section names, singular. */
const KIND_WORDS: Record<ManualMark['kind'], string> = {
line: 'line',
arrowhead: 'arrowhead',
rect: 'rectangle',
}
/**
* How many marks under the pointer a menu names, nearest first.
*
* Three, because a place where more than three marks meet is a place where the
* reader is looking at the marks rather than through them, and a menu that
* grew a row per mark there would push *Hide every mark* — the row that
* answers that moment — off the bottom.
*/
const UNDER_POINTER = 3
/**
* How long a mark's coordinates may run in the hint column before they are
* shortened to the first axis. A box off the diagonal is two loci, and the
* hint column cuts what does not fit at the menu's edge — which cut the part
* that tells two such boxes apart. The whole of it is in the row's tooltip.
*/
const HINT_CHARACTERS = 30
/**
* The group, or nothing where there are no marks at all — a menu offering to
* hide what was never drawn is a row a reader tries and then distrusts.
*
* @param under the drawn marks under the pointer, nearest first.
*/
export function marksMenu(store: ManualStore | undefined, under: readonly ManualMark[] = []): MenuItem[] {
if (store === undefined || store.marks.length === 0) return []
const items: MenuItem[] = [{ label: 'Marks' }]
for (const mark of under.slice(0, UNDER_POINTER)) {
const name = describeMark(mark)
items.push({
label: `Hide this ${KIND_WORDS[mark.kind]}`,
hint: name.length > HINT_CHARACTERS ? `${name.split(' × ')[0]} × …` : name,
title: `${name}, off every map, track and model; its eye on the Manual annotations card brings it back`,
run: () => store.setShown(mark.id, false),
})
}
items.push(allMarksItem(store))
return items
}
/**
* The switch for all of them: *hide every mark* while any is drawn, and *show
* the marks* — exactly the ones it hid — once none is.
*
* Its key in the hint column, where every menu on the reader's machine prints
* one: a row reached for while looking at the picture is the moment a reader
* learns there is a faster way to do it.
*/
function allMarksItem(store: ManualStore): MenuItem {
if (store.anyShown) {
return {
label: 'Hide every mark',
hint: keyLabel(KEYS.marks),
title: withKey('Every line, arrowhead and rectangle, off every map, track and model until shown again', KEYS.marks),
run: () => store.hideAll(),
}
}
return {
label: 'Show the marks',
hint: keyLabel(KEYS.marks),
title: withKey(
store.marks.some((mark) => mark.hidden === 'all')
? 'The marks hidden together, back where they were; one hidden on its own stays hidden'
: 'Every mark, back on every map, track and model',
KEYS.marks,
),
run: () => store.showAll(),
}
}
|