Common props
Every element accepts the props on this page, in addition to its own. Each element’s page lists only what is specific to it. A component you write takes these too, undeclared: a prop it does not name lands on its root element — the fallthrough rule — with one exception, the handlers, which never fall through.
Reference
Section titled “Reference”Geometry
Section titled “Geometry”x: A number of pixels from the parent’s left edge; one of"left","center","right"; a percentage like"25%"; or an anchor with an offset —{ left: n },{ center: n },{ right: n }, wherenis a pixel count or a percentage of the parent ({ right: '15%' }).y: The same vertically:"top","center","bottom", a number, a percentage, or{ top: n },{ center: n },{ bottom: n }.w: A number of pixels;"fill"for the parent’s width;"aspect"to keep the natural proportions givenh; a percentage; or{ fill: n }for the parent’s width lessnpixels.h: The same vertically.right: A number. Inset from the parent’s right edge — the same asx={{ right: n }}.bottom: A number. Inset from the parent’s bottom edge.
<sprite x="left" y="top" w={CORNER} h={CORNER} sprite={topLeft} tiling /><sprite x="right" y={CORNER} w={CORNER} h={{ fill: CORNER * 2 }} sprite={right} tiling /><sprite x={{ right: 3 }} y={6} w={26} h={23} sprite={Chrome.CLOSE} tiling />Layout and geometry has the vocabulary.
Caveats
Section titled “Caveats”- A size takes a pixel inset or a proportion, never both.
{ fill: '10%' }is refused (TS2E210). Write"90%", or{ fill: n }. "aspect"needs the other axis to be something.- A
? :may mix forms:x={i % 2 === 0 ? 0 : 'right'},w={wide === 1 ? { fill: 8 } : 40}.
Visibility
Section titled “Visibility”hide: A boolean. Iftruethe component is not drawn and takes no input. Nothing under a hidden component updates: no timer ticks, no watch fires, until it is shown again. The value may be a comparison —hide={n === 0}.ref: ARef. Binds this component to the ref so a handler can change or measure it. A ref is bound to exactly one component.
The right-click menu
Section titled “The right-click menu”options: An array, in menu order. Each entry is a string, or an object{ label, hotkey, modifiers? }that puts a key on the option. An empty entry leaves that slot open:options={['', '', 'Bank-all']}puts one option third. The list may be written inline, named, or chosen at run time between two named lists. A label may be a run-time string.optionSubject: A string. The noun after the verb — “Wield Rune scimitar”.optionPriority: A number. Whose options come first when two components overlap under the pointer.continueOp: A literaltrue. Offers a dialogue’s “Continue” entry; a click on it is what the server hears.targetVerb: A string. The verb used when this component is the target of a spell or an item.
<sprite x="center" y="center" w={36} h={32} item={item} itemCount={count} options={['Use', '', '', '', 'Drop', '', '', '', '', 'Examine']} />Use is option 1, Drop is 5 and Examine is 10 — the positions the game
puts them in.
<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 with a hotkey is a keyboard-only option: the key fires it, and
the menu shows nothing for it. hotkey is the client’s own key code — Escape
is 13 — and modifiers asks for a held modifier: 1 ctrl, 2 alt, 4 shift.
Hover and menus has the whole
menu.
Caveats
Section titled “Caveats”onOptionSelectwith nooptionsis unreachable. The compiler does not refuse it; write both.- An
optionslist with noonOptionSelectis allowed; the server may answer it. - A left click chooses the first option with a label, the top of the menu; if that option is sixth or later, a left click opens the menu instead.
Interaction flags
Section titled “Interaction flags”The first four take a literal true or false (TS2E221).
continueOp: Above.dragTarget: A dragged component may be dropped onto this one.spellTarget: A selected spell may be cast on this component.allowRunScript: This component may ask the server to run something.spellTargets: An array of"groundItem","npc","object","player","component". What a spell or item on this component may be used on. Pair it withtargetVerb.interactionDepth: A number from 0 to 7. How many parents up the component that actually reacts to a click or drag on this one is;0is this component.noClickThrough: A boolean. Clicks inside this component do not reach whatever is behind it.noScrollThrough: A boolean. Wheel events inside this component do not reach whatever is behind it.
<text w="fill" h={16} text="Drop here" font={Fonts.PLAIN} color={Colors.AMBER} dragTarget={armed === 1} // must be `dragTarget` or nothing onClick={() => { setArmed(1 - armed); }} />draggable: ARefto the container the drag is measured in. The component cannot leave that box, and every position a drag reports is relative to it.dragMoves: A boolean. The component stays where its props put it and nothing is carried under the pointer — a thumb. Without it the client carries a copy of the component and puts it back on release — an item.dragDeadZone: A number of pixels the pointer moves before a press becomes a drag.dragDeadTime: A number of client cycles the button is held before a press becomes a drag.
<sprite x={level * TRAVEL / 100} y={0} w={PIECE} h={PIECE} sprite={Slider.THUMB} draggable={zone} dragMoves dragDeadZone={0} dragDeadTime={0} onDrag={(event) => { setLevel(Math.clamp(0, 100, event.mouseX * 100 / TRAVEL)); }} />Drag and drop has the rest.
hover: An object of prop overrides applied while the pointer is inside the component and undone when it leaves. Any prop of the element may appear in it —sprite,color,hide,transparency— plus one of its own:releaseAfterMs: A number. Hold the override this long after the pointer leaves, including leaving the window.
<sprite x={4} y="center" w={16} h={16} sprite={on === 1 ? Icons.CHECKBOX_ON : Icons.CHECKBOX_OFF} hover={{ sprite: Icons.CHECKBOX_HOVER }} />The value it returns to is whatever the prop says — here a conditional, so a ticked checkbox goes back to ticked.
<rect w="fill" h="fill" color={0x2a2218} fill hover={{ color: 0x5a4a2a, releaseAfterMs: 400 }} />Caveats
Section titled “Caveats”hovercannot be combined withonMouseEnteroronMouseLeaveon the same component (TS2E217).releaseAfterMscannot sit besideonTimeron the same component (TS2E217).
static
Section titled “static”static: A bare flag, or a string name. Makes this component addressable: a box another interface opens into, a component the server can name, a component a handler elsewhere can write to. On a<layer>its children are addressable too. Name it with a module-scopecreateRefbound to itsrefwhen something outside the build addresses it.
<layer static ref={walletSlot} x={12} y={44} w={88} h={126}> <Wallet /></layer><layer static ref={offerSlot} x={112} y={44} w={128} h={126} />Caveats
Section titled “Caveats”- Nothing above it may read state.
staticunder a component that reads state, a conditional, or a handler that captures a local is refused (TS2E224). - Directly inside
<ui>, only a<layer>may bestatic(TS2E302).
contentType
Section titled “contentType”contentType: One of"fpsCounter","worldView","minimap","compass","worldMap","worldMapOverview","loginRunes", or a raw number for a surface the client does not name. Marks the component as one the client draws into itself — the 3D scene, the minimap — rather than something you draw.
rebuildOn
Section titled “rebuildOn”rebuildOn: A whole number. Remake every conditional region among this element’s children whenever the value moves, whether or not a condition changed its answer. For “the server rebuilt this list”: a counter the rows are made afresh from.
Caveats
Section titled “Caveats”- It goes on the element that holds the region. With no
{cond && …}among the children it gates nothing and is refused (TS2E256); on a component call it is refused too (TS2E259). - The key is a whole number. A string key is refused by name (
TS2E258); keep the list in its string cell and give the region a companionIntcell whose only job is to be the key. - A rebuild deletes: refs, focus and hover state inside the region go with it. It does not reach into a nested component’s own regions.
deferred
Section titled “deferred”deferred: A boolean. Leave the interface definition’s own value standing for this element’s state-reading props until the state first moves, rather than writing the current value as the interface opens. For a component whose resting look is the definition’s own until the server first changes it. It is ignored on a prop that reads no state. Not on<ui>.
Handlers
Section titled “Handlers”A handler is an arrow function written on the prop, or an arrow that calls a
function you named. What the player did arrives as its parameter — see
Event — and the parameter is optional.
| Prop | Fires when |
|---|---|
onClick | the player clicks |
onClickRepeat | repeatedly, while the button is held down |
onHold | while the pointer is held on the component |
onRelease | the button is released |
onMouseEnter | the pointer enters |
onMouseLeave | the pointer leaves |
onMouseRepeat | every cycle, while the pointer is inside |
onScrollWheel | the wheel turns over the component |
onOptionSelect | a right-click option is chosen; event.option says which |
onDrag | the component is dragged; event.mouseX/mouseY are inside the draggable container |
onDragComplete | the drag ends; event.dragTarget is what it landed on, or -1 |
onTargetEnter, onTargetLeave | a spell or item on this component is selected as something to use, and unselected — not a drag |
onKey | a key is pressed while the interface is open; event.keyCode, event.keyChar |
onResize | resizing the game window, or the server moving the interface to another slot, changed the component’s size; a ref write does not |
onTimer | every client cycle, for as long as the timer is set — stopTimer clears it |
<layer x={i * 79} y={0} w={74} h={22} onClick={() => { setTab(i); }}>Caveats
Section titled “Caveats”- A handler does not fall through to a component.
<Row onClick={…} />on a component that declares noonClickis refused (TS2E202), even though its root would take one; and a function cannot be a declared prop either. Put the handler on an element the component draws, or on a<layer>around the call. - Write the arrow:
onClick={() => { restoreDefaults(); }}, notonClick={restoreDefaults}. nulltakes a handler off; absent writes nothing.nullon one arm of a conditional, or assigned through a ref, clears a bound handler. An unconditionalonClick={null}on an element is refused (TS2E206).onTimeris the client’s own timer and fires every cycle. PreferuseIntervaloruseDelayunless the step is the cycle.onVarTransmit,onInventoryTransmitandonSkillTransmitare refused (TS2E218): useuseWatch. Nothing bindsonChatTransmit; watchEngine.CHAT.
onLoad
Section titled “onLoad”onLoad: A handler run once, as the interface opens. Its argument is a call to a function of yours whose arguments are constants orevent.self.
<text ref={poisonLabel} x="right" y="center" w={120} h={16} text="Poison: -" font={Fonts.PLAIN} align="right" valign="middle" shadow color={Colors.WHITE} onLoad={(event) => { writePoison(event.self); }} />Caveats
Section titled “Caveats”- Not on an element under a component that reads state, a conditional, or a
handler that captures a local. There it is refused (
TS2E206). - It cannot be
null.
Troubleshooting
Section titled “Troubleshooting”My component is in the corner instead of where I positioned it
Section titled “My component is in the corner instead of where I positioned it”x and y are measured from the parent’s top-left corner. If the parent is
larger than you think — a <layer w="fill" h="fill"> fills the whole interface
— the offsets land somewhere unexpected. Give the parent a size, or anchor the
child with x="center" instead of computing a position.
My panel scrolls the game world as well as itself
Section titled “My panel scrolls the game world as well as itself”Add noScrollThrough to the panel.
My right-click option does not appear
Section titled “My right-click option does not appear”Check that options is set, not only onOptionSelect. If the option is the
server’s to answer, options alone is correct and there should be no
onOptionSelect.
Nothing under my hidden layer updates when I show it again
Section titled “Nothing under my hidden layer updates when I show it again”If the container carries a read or a timer that must keep running, put
the hide on the children instead.
static is refused
Section titled “static is refused”Something between the slot and the interface is built at run time. The message names the part — usually a component that reads state, a conditional, or a handler that captures a local. Move the slot above that part, or take the read out of it.