Skip to content

Drawer

Drawer (from scenerystack/scenery-phet) wraps an arbitrary contentsNode in a clipped container with a clickable handle: clicking the handle slides the contents in and out of view (by default, animated), the way a physical drawer opens and closes. It's used wherever a sim wants to tuck away optional content — a mini legend, a secondary control — behind a single, low-profile handle instead of an AccordionBox's title bar.

ts
import { Drawer } from 'scenerystack/scenery-phet';
import { Text } from 'scenerystack/scenery';

A minimal example

ts
const contentsNode = new Text( 'Extra info goes here' );

const drawer = new Drawer( contentsNode, {
  open: false,
  handlePosition: 'top',
  tandem: tandem.createTandem( 'drawer' )
} );

screenView.addChild( drawer );

// Open/close programmatically:
drawer.openProperty.value = true;

Clicking the handle toggles drawer.openProperty; toggling openProperty yourself (or via PhET-iO) drives the same animation the handle click would.

Constructor

ts
new Drawer( contentsNode: Node, provideOptions?: DrawerOptions )

Options

OptionDefaultEffect
sizenullIf provided, the container is fixed at this Dimension2 and contentsNode is scaled down (never up) to fit; if null, the container is sized to fit contentsNode exactly
cornerRadius0Corner radius of the container's background/border
xMargin / yMargin0 / 0Margin between contentsNode and the container edge, before any size-driven scaling
opentrueWhether the drawer starts open
handlePosition'top''top' or 'bottom' — which edge the handle (and the rounded corners on it) is drawn on
handleSizeDimension2( 70, 20 )Size of the handle bar
handleFill'rgb( 230, 230, 230 )'Fill color of the handle
handleTouchAreaXDilation / handleTouchAreaYDilation0 / 0Touch-area dilation around the handle
grippyDotRows / grippyDotColumns2 / 4Layout of the small "grippy" dots drawn on the handle
beforeOpenmakes contentsNode visibleCalled immediately before opening starts
afterClosehides contentsNodeCalled immediately after closing finishes — the default behavior is why the drawer's content can safely keep updating only while visible
animationEnabledtrueWhether opening/closing is animated at all; false snaps instantly
animationDuration0.5Duration of the open/close animation, in seconds, using Animation's QUADRATIC_IN_OUT easing

Public API

MemberDescription
openPropertyProperty<boolean> — the drawer's open/closed state; settable directly or via the handle
contentsNodeThe Node passed to the constructor
reset( animationEnabled? )Returns openProperty to its initial value, optionally overriding whether that reset itself is animated
animationEnabled (get/set)Whether future open/close transitions are animated

beforeOpen / afterClose are how contentsNode avoids updating while hidden

By default, Drawer makes contentsNode invisible only after the close animation finishes, and visible again before the open animation starts — so if contentsNode's own updates are gated on node.visible, they naturally pause while the drawer is fully closed. Overriding beforeOpen/afterClose replaces this behavior entirely, so re-implement the visibility toggle yourself if you still want it.

Drawer's handle is a distinct graphic from HandleNode

Drawer draws its own small rounded pull-tab with dots — it does not compose a HandleNode internally, and there's no option to swap in one. If you need HandleNode's grip-and-attachment look specifically, you'll need a custom container rather than Drawer.