Skip to content

Hover and menus

Two props cover the most common interactions: hover for an appearance under the pointer, and options for the right-click menu.

hover overrides props while the pointer is inside the component, and undoes them when it leaves:

src/settings.ts2
<sprite x={4} y="center" w={16} h={16}
sprite={on === 1 ? Icons.CHECKBOX_ON : Icons.CHECKBOX_OFF}
hover={{ sprite: Icons.CHECKBOX_HOVER }} />

releaseAfterMs holds the override that long after the pointer leaves:

src/menu.ts2
<rect w="fill" h="fill" color={0x2a2218} fill
hover={{ color: 0x5a4a2a, releaseAfterMs: 400 }} />

For a button made of several pieces, put onMouseEnter and onMouseLeave on the container, write a cell, and let the pieces read it:

src/menu.ts2
const [hovered, setHovered] = useState(0);
return (
<layer x={x} y={0} w={w} h={22}
onMouseEnter={() => { setHovered(1); }}
onMouseLeave={() => { setHovered(0); }}>
{/* … */}
color={hovered === 1 ? Colors.WHITE : Colors.AMBER} />

options gives a component menu entries, in menu order. An empty string leaves a slot open, so here Drop is fifth and Examine last:

src/inventory.ts2
<sprite x="center" y="center" w={36} h={32} item={item} itemCount={count}
options={['Use', '', '', '', 'Drop', '', '', '', '', 'Examine']} />

The list can be decided at run time. Name the lists, and choose between them where the component is drawn:

src/menu.ts2
const OPEN_OPTIONS = ['Wield', '', '', '', 'Drop', '', '', '', '', 'Examine'];
const LOCKED_OPTIONS = ['', '', '', '', '', '', '', '', '', 'Examine'];
src/menu.ts2
<layer x={4} y="center" w={36} h={32}
optionSubject="Rune scimitar" optionPriority={1}
options={locked === 1 ? LOCKED_OPTIONS : OPEN_OPTIONS}
onOptionSelect={locked === 1 ? null : (event) => { setChosen(nameOf(event.option)); }}>
<sprite x="center" y="center" w={36} h={32} item={Items.RUNE_SCIMITAR} itemCountMode="never" />
</layer>

onOptionSelect fires when any option is chosen; event.option says which, numbered from 1. optionSubject is the noun after the verb (“Wield Rune scimitar”), and optionPriority decides whose options come first when two components overlap.

An option with no onOptionSelect goes to the server when chosen, which is how buying an item or accepting a trade works. It still needs options. continueOp offers a dialogue’s “Click here to continue” entry the same way.

An option may carry a hotkey, written as an object with the client’s key code (Escape is 13). The key fires the option as a click would:

src/menu.ts2
<text x={0} y={84} w="fill" h={16} font={Fonts.PLAIN}
text={dismissed === 1 ? 'Dismissed' : 'Click here to continue'}
align="center" valign="middle" shadow color={0x0000ff} continueOp
options={[{ label: '', hotkey: KEY_ESCAPE }]}
onOptionSelect={() => { setDismissed(1); }} />

An empty label makes the option keyboard-only and keeps it out of the menu. modifiers asks for a held modifier: 1 for ctrl, 2 for alt, 4 for shift.