Skip to content

SliderWidget

Defined in: src/widgets/controls/slider.ts:8

Base class for every visual node in the tree, and the extension point for custom widgets. Subclass it, override the methods below, then make it usable: registerElement("ztui-mywidget", () => new MyWidget()) (from ztui) and, for JSX, hostComponent("ztui-mywidget") (from ztui/react). See the “Extending ztui” guide.

The lifecycle each frame is measure → layout → render:

  • measure computes your intrinsic size from the offered space (called bottom-up, so children are measured first).
  • The layout engine assigns your region from your styles.
  • render paints cells into the ScreenBuffer for your region.

Input arrives through handleKey / handleMouse / handleScroll when the widget is focused or hit-tested; set focusable to take keyboard focus. onMount / onUnmount bracket the widget’s time in the live tree.

The override methods listed here form the stable extension contract. Anything not documented as overridable is internal plumbing and may change.

new SliderWidget(): SliderWidget

Defined in: src/widgets/controls/slider.ts:34

SliderWidget

Widget.constructor

children: DOMNode[] = []

Defined in: src/dom/dom.ts:16

Child nodes in document order.

Widget.children


classes: Set<string>

Defined in: src/dom/dom.ts:10

Advisory class names (no CSS cascade today).

Widget.classes


defaultStyle: WidgetStyles = {}

Defined in: src/dom/widget.ts:201

The widget’s built-in look, overridden by style. Set by subclasses.

Widget.defaultStyle


disabled: boolean = false

Defined in: src/dom/widget.ts:315

When true, this widget (and its descendants) are inert: not focusable, they ignore key/mouse input, and interactive controls render in a muted style. Set via the disabled prop. Checked through isDisabled so disabling a container disables everything inside it.

Widget.disabled


focusable: boolean = false

Defined in: src/dom/widget.ts:291

Whether this widget can take keyboard focus.

Widget.focusable


focused: boolean = false

Defined in: src/dom/widget.ts:306

True while this widget holds keyboard focus.

Widget.focused


focusOnClick: boolean = false

Defined in: src/dom/widget.ts:298

When a click lands on this (non-focusable) container — its padding, border, or any non-focusable child — move focus to its first focusable descendant. Lets a Form/Panel/Box act like one click target that hands focus to its first field. Off by default.

Widget.focusOnClick


hoverInterest: boolean = false

Defined in: src/dom/widget.ts:197

Opt-in hint: this widget visually or behaviorally cares about passive hover motion.

Widget.hoverInterest


id: string = ""

Defined in: src/dom/dom.ts:8

Optional stable identifier (from the id prop).

Widget.id


optional label?: string

Defined in: src/dom/widget.ts:344

Structural/accessible label; also the tab title inside TabContainer.

Widget.label


max: number = 100

Defined in: src/widgets/controls/slider.ts:18

Maximum value.


measuredHeight: number = 0

Defined in: src/dom/widget.ts:287

Intrinsic height from the last measure.

Widget.measuredHeight


measuredWidth: number = 0

Defined in: src/dom/widget.ts:285

Intrinsic width from the last measure, before layout distributes space.

Widget.measuredWidth


min: number = 0

Defined in: src/widgets/controls/slider.ts:16

Minimum value.


optional onClick?: (ev) => void

Defined in: src/dom/widget.ts:356

Pointer click handler.

any

void

Widget.onClick


optional onDragEnd?: (x, y, moved) => void

Defined in: src/dom/widget.ts:380

Drag released; moved is false for a tap with no movement.

number

number

boolean

void

Widget.onDragEnd


optional onDragMove?: (x, y) => void

Defined in: src/dom/widget.ts:378

Pointer moved while dragging from this widget.

number

number

void

Widget.onDragMove


optional onDragStart?: (x, y) => void

Defined in: src/dom/widget.ts:376

Drag began on this widget (pointer pressed).

number

number

void

Widget.onDragStart


optional onKey?: (ev) => void

Defined in: src/dom/widget.ts:365

Key handler invoked while focused (the base handleKey forwards here).

any

void

Widget.onKey


optional onMouseDown?: (ev) => void

Defined in: src/dom/widget.ts:363

Pointer pressed on this widget, for any button — fired on press before the left-button focus/onClick path. The event carries button and x/y, so this is how to react to a right-click (e.g. open a context menu). Set ev.handled = true to suppress the default focus/click.

MouseEvent

void

Widget.onMouseDown


optional onMouseEnter?: (ev) => void

Defined in: src/dom/widget.ts:369

Called when the pointer enters this widget’s region.

any

void

Widget.onMouseEnter


optional onMouseLeave?: (ev) => void

Defined in: src/dom/widget.ts:371

Called when the pointer leaves this widget’s region.

any

void

Widget.onMouseLeave


optional onScroll?: (ev) => void

Defined in: src/dom/widget.ts:367

Wheel/scroll handler (the base handleScroll forwards here).

MouseEvent

void

Widget.onScroll


parent: DOMNode | null = null

Defined in: src/dom/dom.ts:14

Parent node, or null when detached / at the root.

Widget.parent


pointerTransparent: boolean = false

Defined in: src/dom/widget.ts:304

This widget (and its subtree) never captures the pointer — hit-testing falls through to whatever is beneath. For decorative full-screen overlays like the DevTools highlight. CSS pointer-events: none. Off by default.

Widget.pointerTransparent


positionFixed: boolean = false

Defined in: src/dom/widget.ts:335

When true, an absolutely-positioned child is pinned to its parent’s viewport and is NOT shifted by the parent’s scroll offset (CSS position: fixed semantics). Used by overlay chrome like the copy button so it stays in the top-right corner instead of scrolling away with the content.

Widget.positionFixed


prevRegion: Region = Region.EMPTY

Defined in: src/dom/widget.ts:283

The region as of the last laid-out frame, for geometry-stability detection: a queueRepaintWidget may scope a frame to one widget only when a fresh layout leaves every region equal to its prevRegion (nothing moved). Updated by the App after each layout pass.

Widget.prevRegion


region: Region = Region.EMPTY

Defined in: src/dom/widget.ts:276

This widget’s rectangle in terminal cells, assigned by the layout engine.

Widget.region


scrollOffset: Offset = Offset.ORIGIN

Defined in: src/dom/widget.ts:289

Scroll position of this widget’s content (see Scrollable).

Widget.scrollOffset


selectable: boolean = true

Defined in: src/dom/widget.ts:328

Whether this widget’s selectable content participates in read-only text selection. Default true; set false on chrome leaves (e.g. Markdown list bullets / horizontal rules) so they register no content runs and are skipped.

Widget.selectable


selectionContainer: boolean = false

Defined in: src/dom/widget.ts:322

When true, a read-only text selection started on any descendant is anchored to this widget’s region instead of the leaf, so a drag can span the composed children (e.g. selecting across the paragraphs/code blocks a Markdown widget renders into separate leaves). See widgets/readonly-selection.

Widget.selectionContainer


selectionRaw: string | null = null

Defined in: src/dom/widget.ts:342

Original source text for this subtree (e.g. the raw markdown of the block a Markdown widget rendered it from). When a read-only selection fully covers the subtree’s content, copy emits this verbatim instead of the rendered text, so copied markdown round-trips its formatting.

Widget.selectionRaw


step: number = 1

Defined in: src/widgets/controls/slider.ts:20

Increment per step.


style: WidgetStyles = {}

Defined in: src/dom/widget.ts:199

Author-set inline styles; override the widget’s defaultStyle key-by-key.

Widget.style


tagName: string = ""

Defined in: src/dom/dom.ts:12

Host element tag, lowercased (e.g. “ztui-button”).

Widget.tagName


readonly validation: FieldValidation

Defined in: src/widgets/controls/slider.ts:24

Validation; the validated value is the numeric value.


value: number = 0

Defined in: src/widgets/controls/slider.ts:14

Current value.


visible: boolean = true

Defined in: src/dom/widget.ts:308

Whether this widget renders and participates in layout.

Widget.visible

get app(): WidgetApp | null

Defined in: src/dom/widget.ts:267

The App that owns this widget, found by walking the parent chain to the tree root (the App node sets screen.parent = this). Returns null when the widget is detached (e.g. mid-construction, before it’s mounted).

Prefer this over the App.instance singleton for anything that must act on this widget’s app — style resolution, render scheduling — so multiple live apps (tests, the web backend) don’t resolve each other’s trees.

WidgetApp | null

Widget.app


get borderSize(): Spacing

Defined in: src/dom/widget.ts:538

Border thickness as a Spacing — 1 per side that has a border, else zero.

Spacing

Widget.borderSize


get computedStyle(): WidgetStyles

Defined in: src/dom/widget.ts:207

Fully resolved styles for this frame ($tokens, hover/focus, defaults folded in). Read this in render, not style.

WidgetStyles

set computedStyle(val): void

Defined in: src/dom/widget.ts:211

Set by the engine each frame after resolving styles; you rarely set this yourself.

WidgetStyles

void

Widget.computedStyle


get cursorShape(): "default" | "move" | "pointer" | "text" | "vertical-text" | "wait" | "progress" | "help" | "crosshair" | "cell" | "grab" | "grabbing" | "alias" | "copy" | "no-drop" | "not-allowed" | "zoom-in" | "zoom-out" | "n-resize" | "e-resize" | "s-resize" | "w-resize" | "ne-resize" | "nw-resize" | "se-resize" | "sw-resize" | "ew-resize" | "ns-resize" | "nesw-resize" | "nwse-resize" | null

Defined in: src/dom/widget.ts:233

The mouse-pointer shape shown over this widget, or null (default arrow). An explicit cursor style wins (resolved style first, so a :hover rule applies); otherwise the role-based defaultCursor is used. A disabled interactive widget always reports "not-allowed".

"default" | "move" | "pointer" | "text" | "vertical-text" | "wait" | "progress" | "help" | "crosshair" | "cell" | "grab" | "grabbing" | "alias" | "copy" | "no-drop" | "not-allowed" | "zoom-in" | "zoom-out" | "n-resize" | "e-resize" | "s-resize" | "w-resize" | "ne-resize" | "nw-resize" | "se-resize" | "sw-resize" | "ew-resize" | "ns-resize" | "nesw-resize" | "nwse-resize" | null

Widget.cursorShape


get margin(): Spacing

Defined in: src/dom/widget.ts:485

This widget’s margin as a normalized Spacing.

Spacing

Widget.margin


get padding(): Spacing

Defined in: src/dom/widget.ts:496

This widget’s padding as a normalized Spacing.

Spacing

Widget.padding


get theme(): string | undefined

Defined in: src/dom/widget.ts:347

Theme name applied to this subtree; descendants resolve $tokens against it.

string | undefined

set theme(val): void

Defined in: src/dom/widget.ts:351

Apply a theme name to this subtree (descendants resolve $tokens against it).

string | undefined

void

Widget.theme

animate(key, target, opts?): number

Defined in: src/dom/widget.ts:606

Drive a named scalar tween toward target, returning the value to show this frame. Call it from render: while the tween is still moving it books the next animation frame on this widget, so the value advances on its own without any external clock or state. This is the framework-agnostic counterpart to React’s useAnimatedValue — the engine lives on the widget, so every binding gets smooth motion for the same call.

A non-positive duration (or opts omitted with a default of 0 from the caller) snaps immediately, so animation can be turned off by passing duration: 0.

string

number

TweenOptions

number

Widget.animate


animateColor(key, target, opts?): string

Defined in: src/dom/widget.ts:628

Colour counterpart to animate: tweens a named CSS colour toward target, returning the rgb(...) string to paint this frame and booking the next frame while in flight.

string

string

TweenOptions

string

Widget.animateColor


appendChild(child): void

Defined in: src/dom/dom.ts:23

Append child, detaching it from any previous parent.

DOMNode

void

Widget.appendChild


cursorShapeAt(_x, _y): "default" | "move" | "pointer" | "text" | "vertical-text" | "wait" | "progress" | "help" | "crosshair" | "cell" | "grab" | "grabbing" | "alias" | "copy" | "no-drop" | "not-allowed" | "zoom-in" | "zoom-out" | "n-resize" | "e-resize" | "s-resize" | "w-resize" | "ne-resize" | "nw-resize" | "se-resize" | "sw-resize" | "ew-resize" | "ns-resize" | "nesw-resize" | "nwse-resize" | null

Defined in: src/dom/widget.ts:249

The pointer shape at a specific cell (absolute screen coordinates), letting a widget vary the cursor across its own area — e.g. a list returns its pointer over rows but the default arrow over its scrollbar gutter. The base ignores position and returns cursorShape; override for sub-region control. Returning null defers to the ancestor / default arrow.

number

number

"default" | "move" | "pointer" | "text" | "vertical-text" | "wait" | "progress" | "help" | "crosshair" | "cell" | "grab" | "grabbing" | "alias" | "copy" | "no-drop" | "not-allowed" | "zoom-in" | "zoom-out" | "n-resize" | "e-resize" | "s-resize" | "w-resize" | "ne-resize" | "nw-resize" | "se-resize" | "sw-resize" | "ew-resize" | "ns-resize" | "nesw-resize" | "nwse-resize" | null

Widget.cursorShapeAt


describe(): string

Defined in: src/dom/dom.ts:59

Human/LLM-readable identity for logs and diagnostics, e.g. button#submit.primary @ (2,1 10x1). Includes the laid-out region when one is present (widgets), and the text preview for text nodes.

string

Widget.describe


findResolvedBackground(): string

Defined in: src/dom/widget.ts:645

This widget’s effective background, walking up to ancestors when unset — used to composite translucent fills.

string

Widget.findResolvedBackground


getAccessibleNode(): AccessibleNode | null

Defined in: src/dom/widget.ts:685

A semantic snapshot of this widget for accessibility / non-visual output (see Screen.toAccessibleText). Returns null to be skipped — the default for pure layout containers that carry no inline text — so the accessible tree shows meaningful nodes (controls, labels, headings) without the structural scaffolding around them.

The base infers a reasonable node from the tag, inline text, focus/disabled state, and a structurally-read checked/value/label. Interactive widgets that know more (a slider’s range, a select’s options) should override to add value/state, calling super.getAccessibleNode() for the common fields.

AccessibleNode | null

Widget.getAccessibleNode


getClientRect(): Region

Defined in: src/dom/widget.ts:548

The region inside the margin (the widget’s visible box, border included).

Region

Widget.getClientRect


getContentRect(): Region

Defined in: src/dom/widget.ts:559

The drawable region inside margin + border + padding — paint custom content here.

Region

Widget.getContentRect


getTextContent(): string

Defined in: src/dom/widget.ts:663

Concatenates the text of all direct TextNode children.

This is the canonical way widgets read their inline JSX text content. Subclasses may override to add behavior (e.g. trimming, or falling back to a label field), typically by post-processing super.getTextContent().

string

Widget.getTextContent


handleKey(ev): void

Defined in: src/dom/widget.ts:430

Handle a key event while this widget is focused. Override to implement keyboard interaction; mark ev.handled = true for keys you consume so they don’t fall through to global hotkeys. The base forwards to onKey. Requires focusable to be true to receive focus.

KeyEvent

void

Widget.handleKey


handleMouse(ev): void

Defined in: src/widgets/controls/slider.ts:67

Handle a mouse event hit-tested to this widget (press/release/drag/move). Override for click/drag interaction; call super.handleMouse(ev) to keep the built-in drag-source lifecycle (onDragStart/onDragMove/onDragEnd). Set ev.handled = true to consume the event.

any

void

Widget.handleMouse


handleScroll(ev): void

Defined in: src/dom/widget.ts:417

Handle a wheel/scroll event. Override to scroll your own content; set ev.handled = true to stop it bubbling. The base forwards to onScroll.

MouseEvent

void

Widget.handleScroll


hasCursorStyle(): boolean

Defined in: src/dom/widget.ts:254

Whether the pointer takes on any shape over this widget (style or role).

boolean

Widget.hasCursorStyle


insertBefore(child, before): void

Defined in: src/dom/dom.ts:41

Insert child before the before sibling (appends if before isn’t found).

DOMNode

DOMNode

void

Widget.insertBefore


isDisabled(): boolean

Defined in: src/dom/widget.ts:575

True when this widget or any ancestor is disabled, so a disabled container (e.g. a <Form disabled>) propagates to every control inside it.

boolean

Widget.isDisabled


matchesSelector(selector): boolean

Defined in: src/dom/dom.ts:87

Match a compound selector against this node — tags, IDs, and classes (e.g. tag#id.a.b).

string

boolean

Widget.matchesSelector


measure(maxW, maxH): void

Defined in: src/widgets/controls/slider.ts:98

Compute this widget’s intrinsic size into measuredWidth / measuredHeight, given the space the parent offers (maxW/maxH). Runs bottom-up (children first). Override point for content-sized custom widgets (e.g. measure your text); call super.measure(maxW, maxH) first if you also have children. Clamp to the offered space so you never overflow.

number

number

void

Widget.measure


onMount(): void

Defined in: src/dom/widget.ts:970

Called once when the widget enters the live tree. Override to start timers, subscribe to stores, or kick off async loads; pair cleanup in onUnmount.

void

Widget.onMount


onUnmount(): void

Defined in: src/dom/widget.ts:973

Called once when the widget leaves the tree. Override to release whatever onMount acquired (timers, subscriptions) so nothing leaks.

void

Widget.onUnmount


removeChild(child): void

Defined in: src/dom/dom.ts:32

Remove child if it’s a child of this node.

DOMNode

void

Widget.removeChild


render(buffer): void

Defined in: src/widgets/controls/slider.ts:118

Paint this widget into the cell buffer. The base draws the background, border, and children. Custom-widget override point: call super.render(buffer) to keep background/border, then paint your content within getContentRect using buffer.setCell(...). Stay inside your region — the parent clips children to its content box by default. Keep render pure and fast: it runs every frame and must not mutate the tree.

ScreenBuffer

void

Widget.render


renderChildren(buffer): void

Defined in: src/dom/widget.ts:912

Paint child widgets (z-index ordered, clipped to the content box). The base render already calls this; override only for unusual child handling (e.g. a custom scroll transform). Most custom widgets don’t need to.

ScreenBuffer

void

Widget.renderChildren


walk(callback): void

Defined in: src/dom/dom.ts:74

Depth-first visit of this node and its descendants, in z-index order.

(node) => void

void

Widget.walk


wantsTab(_ev): boolean

Defined in: src/dom/widget.ts:446

Return true to claim a Tab/Shift+Tab press before it triggers focus traversal. The app consults the focused widget; when it returns true the key is dispatched to handleKey (to consume it) instead of moving focus. Default false, so Tab navigates as usual — override only for the states where Tab does in-widget work (e.g. accepting an open completion or inline suggestion), and return false again once there’s nothing to accept so the next Tab moves on.

KeyEvent

boolean

Widget.wantsTab