@termuijs/widgets 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,469 @@
1
+ import { Style, Rect, EventEmitter, KeyEvent, MouseEvent, LayoutNode, Screen, Color } from '@termuijs/core';
2
+
3
+ /**
4
+ * Event map for widgets.
5
+ */
6
+ interface WidgetEvents {
7
+ key: KeyEvent;
8
+ mouse: MouseEvent;
9
+ focus: void;
10
+ blur: void;
11
+ mount: void;
12
+ unmount: void;
13
+ }
14
+ /**
15
+ * Base class for all TermUI widgets.
16
+ *
17
+ * Provides:
18
+ * - Unique ID generation
19
+ * - Style management and merging
20
+ * - Layout node generation with rect sync
21
+ * - Border/padding rendering into the screen buffer
22
+ * - Child management
23
+ * - Focus support
24
+ * - Event emission
25
+ */
26
+ declare abstract class Widget {
27
+ /** Unique widget identifier */
28
+ readonly id: string;
29
+ /** Widget's style */
30
+ protected _style: Style;
31
+ /** Child widgets */
32
+ protected _children: Widget[];
33
+ /** Parent widget (null for root) */
34
+ parent: Widget | null;
35
+ /** Computed layout rectangle */
36
+ protected _rect: Rect;
37
+ /** Reference to the layout node (set during getLayoutNode) */
38
+ private _layoutNode;
39
+ /** Whether this widget can receive focus */
40
+ focusable: boolean;
41
+ /** Tab index for focus ordering */
42
+ tabIndex: number;
43
+ /** Event emitter for this widget */
44
+ readonly events: EventEmitter<WidgetEvents>;
45
+ /** Whether the widget is currently focused */
46
+ isFocused: boolean;
47
+ /**
48
+ * Dirty flag — true when this widget needs re-rendering.
49
+ * Newly created widgets start dirty.
50
+ */
51
+ protected _dirty: boolean;
52
+ constructor(style?: Partial<Style>);
53
+ /** Get the current style */
54
+ get style(): Style;
55
+ /** Update the style (merge with existing) */
56
+ setStyle(style: Partial<Style>): void;
57
+ /** Get the computed rect after layout */
58
+ get rect(): Rect;
59
+ /** Add a child widget */
60
+ addChild(child: Widget): void;
61
+ /** Remove a child widget */
62
+ removeChild(child: Widget): void;
63
+ /** Remove all children */
64
+ clearChildren(): void;
65
+ /** Get all children */
66
+ get children(): ReadonlyArray<Widget>;
67
+ /**
68
+ * Build the LayoutNode tree for this widget.
69
+ * Stores a reference so we can sync computed rects back via syncLayout().
70
+ */
71
+ getLayoutNode(): LayoutNode;
72
+ /**
73
+ * After computeLayout() has been called, sync the computed rects
74
+ * from the layout tree back into widget `_rect` fields.
75
+ * This MUST be called after computeLayout() and before render().
76
+ */
77
+ syncLayout(): void;
78
+ /**
79
+ * Render this widget (and children) into the screen buffer.
80
+ * Automatically pushes a clip region if overflow is hidden (default).
81
+ */
82
+ render(screen: Screen): void;
83
+ /**
84
+ * Override this to render the widget's own content.
85
+ * The rect is available as `this._rect`.
86
+ */
87
+ protected abstract _renderSelf(screen: Screen): void;
88
+ /**
89
+ * Update the computed rect from layout results.
90
+ */
91
+ updateRect(rect: Rect): void;
92
+ /**
93
+ * Mark this widget as needing re-render.
94
+ * Propagates up to parent so the render loop can detect changes.
95
+ */
96
+ markDirty(): void;
97
+ /**
98
+ * Clear the dirty flag after rendering.
99
+ */
100
+ clearDirty(): void;
101
+ /** Check if this widget (or any child) needs re-rendering */
102
+ get isDirty(): boolean;
103
+ /**
104
+ * Render the border around this widget, including focus ring if focused.
105
+ */
106
+ protected _renderBorder(screen: Screen): void;
107
+ /**
108
+ * Get the inner content area (after border + padding).
109
+ */
110
+ protected _getContentRect(): Rect;
111
+ /**
112
+ * Check if a point hits this widget.
113
+ */
114
+ hitTest(x: number, y: number): boolean;
115
+ /** Lifecycle: called when the widget is mounted */
116
+ mount(): void;
117
+ /** Lifecycle: called when the widget is unmounted */
118
+ unmount(): void;
119
+ }
120
+
121
+ /**
122
+ * Box — the fundamental container widget, similar to a `<div>`.
123
+ *
124
+ * Supports:
125
+ * - Flexbox layout (row/column)
126
+ * - Border styles (single/double/round/heavy/dashed)
127
+ * - Padding and margin
128
+ * - Background color
129
+ */
130
+ declare class Box extends Widget {
131
+ constructor(style?: Partial<Style>);
132
+ protected _renderSelf(screen: Screen): void;
133
+ }
134
+
135
+ interface TextProps {
136
+ content: string;
137
+ wrap?: boolean;
138
+ align?: 'left' | 'center' | 'right';
139
+ }
140
+ /**
141
+ * Text — renders a string of text with word-wrapping and alignment.
142
+ */
143
+ declare class Text extends Widget {
144
+ private _content;
145
+ private _wrap;
146
+ private _align;
147
+ constructor(content: string, style?: Partial<Style>, props?: Partial<TextProps>);
148
+ /** Update the text content */
149
+ setContent(content: string): void;
150
+ /** Get current text content */
151
+ getContent(): string;
152
+ protected _renderSelf(screen: Screen): void;
153
+ }
154
+
155
+ interface LogViewOptions {
156
+ /** Highlight rules: keyword → color */
157
+ highlight?: Record<string, Color>;
158
+ /** Auto-scroll to bottom */
159
+ autoScroll?: boolean;
160
+ }
161
+ /**
162
+ * LogView — scrollable, highlighted log output.
163
+ *
164
+ * Supports keyword-based color highlighting (ERROR → red, WARN → yellow, etc.)
165
+ */
166
+ declare class LogView extends Widget {
167
+ private _lines;
168
+ private _scrollOffset;
169
+ private _highlight;
170
+ private _autoScroll;
171
+ constructor(style?: Partial<Style>, opts?: LogViewOptions);
172
+ setLines(lines: string[]): void;
173
+ appendLine(line: string): void;
174
+ scrollUp(n?: number): void;
175
+ scrollDown(n?: number): void;
176
+ private _scrollToBottom;
177
+ protected _renderSelf(screen: Screen): void;
178
+ private _getLineColor;
179
+ }
180
+
181
+ interface ListItem {
182
+ label: string;
183
+ value: string;
184
+ disabled?: boolean;
185
+ }
186
+ /**
187
+ * List — a scrollable, selectable list of items.
188
+ *
189
+ * Supports:
190
+ * - Keyboard navigation (up/down/Home/End)
191
+ * - Scrolling when items exceed visible height
192
+ * - Custom item styling
193
+ * - Disabled items
194
+ */
195
+ declare class List extends Widget {
196
+ private _items;
197
+ private _selectedIndex;
198
+ private _scrollOffset;
199
+ private _onSelect?;
200
+ constructor(items: ListItem[], style?: Partial<Style>, onSelect?: (item: ListItem, index: number) => void);
201
+ get selectedIndex(): number;
202
+ get selectedItem(): ListItem | undefined;
203
+ setItems(items: ListItem[]): void;
204
+ /** Move selection up */
205
+ selectPrev(): void;
206
+ /** Move selection down */
207
+ selectNext(): void;
208
+ /** Confirm the current selection */
209
+ confirm(): void;
210
+ protected _renderSelf(screen: Screen): void;
211
+ private _clampScroll;
212
+ }
213
+
214
+ /**
215
+ * TextInput — a single-line text input field.
216
+ *
217
+ * Supports:
218
+ * - Cursor movement (left/right/Home/End)
219
+ * - Character insertion and deletion
220
+ * - Placeholder text
221
+ * - Password masking
222
+ * - Max length constraint
223
+ */
224
+ declare class TextInput extends Widget {
225
+ private _value;
226
+ private _cursorPos;
227
+ private _placeholder;
228
+ private _mask;
229
+ private _maxLength;
230
+ private _onChange?;
231
+ private _onSubmit?;
232
+ constructor(style?: Partial<Style>, options?: {
233
+ placeholder?: string;
234
+ mask?: string;
235
+ maxLength?: number;
236
+ onChange?: (value: string) => void;
237
+ onSubmit?: (value: string) => void;
238
+ });
239
+ get value(): string;
240
+ set value(v: string);
241
+ /**
242
+ * Handle a typed character.
243
+ */
244
+ insertChar(char: string): void;
245
+ /**
246
+ * Delete the character before the cursor.
247
+ */
248
+ deleteBack(): void;
249
+ /**
250
+ * Delete the character after the cursor.
251
+ */
252
+ deleteForward(): void;
253
+ moveCursorLeft(): void;
254
+ moveCursorRight(): void;
255
+ moveCursorHome(): void;
256
+ moveCursorEnd(): void;
257
+ submit(): void;
258
+ clear(): void;
259
+ protected _renderSelf(screen: Screen): void;
260
+ }
261
+
262
+ interface TableColumn {
263
+ /** Column header label */
264
+ header: string;
265
+ /** Key to pull data from row objects */
266
+ key: string;
267
+ /** Fixed width (chars). If omitted, auto-distributes. */
268
+ width?: number;
269
+ /** Text alignment within the column */
270
+ align?: 'left' | 'center' | 'right';
271
+ }
272
+ type TableRow = Record<string, string | number>;
273
+ interface TableOptions {
274
+ /** Whether to show the header row */
275
+ showHeader?: boolean;
276
+ /** Color for the header row */
277
+ headerColor?: Color;
278
+ /** Whether rows are zebra-striped */
279
+ stripe?: boolean;
280
+ /** Stripe color */
281
+ stripeColor?: Color;
282
+ /** Column separator character */
283
+ separator?: string;
284
+ }
285
+ /**
286
+ * Table — renders tabular data with columns, headers, and optional zebra-striping.
287
+ *
288
+ * Supports:
289
+ * - Auto-width column distribution
290
+ * - Fixed and percentage widths
291
+ * - Header styling
292
+ * - Zebra striping
293
+ * - Text alignment per column
294
+ * - Truncation for overflow
295
+ */
296
+ declare class Table extends Widget {
297
+ private _columns;
298
+ private _rows;
299
+ private _showHeader;
300
+ private _headerColor;
301
+ private _stripe;
302
+ private _stripeColor;
303
+ private _separator;
304
+ constructor(columns: TableColumn[], rows: TableRow[], style?: Partial<Style>, options?: TableOptions);
305
+ setRows(rows: TableRow[]): void;
306
+ protected _renderSelf(screen: Screen): void;
307
+ private _computeColumnWidths;
308
+ private _alignText;
309
+ }
310
+
311
+ interface GaugeOptions {
312
+ /** Color of the filled portion */
313
+ color?: Color;
314
+ /** Show percentage label */
315
+ showLabel?: boolean;
316
+ }
317
+ /**
318
+ * Gauge — a self-contained metric display with label, bar, and value.
319
+ *
320
+ * Example:
321
+ * CPU ████████░░░░ 65%
322
+ */
323
+ declare class Gauge extends Widget {
324
+ private _label;
325
+ private _value;
326
+ private _color;
327
+ private _showLabel;
328
+ constructor(label: string, style?: Partial<Style>, opts?: GaugeOptions);
329
+ setValue(value: number): void;
330
+ getValue(): number;
331
+ setLabel(label: string): void;
332
+ protected _renderSelf(screen: Screen): void;
333
+ }
334
+
335
+ interface SparklineOptions {
336
+ /** Color of the sparkline */
337
+ color?: Color;
338
+ /** Show min/max labels */
339
+ showRange?: boolean;
340
+ }
341
+ /**
342
+ * Sparkline — a compact inline chart showing a data trend.
343
+ *
344
+ * Example:
345
+ * Latency ▂▃▅▇▅▃▂▁▃▅█▅▃
346
+ */
347
+ declare class Sparkline extends Widget {
348
+ private _label;
349
+ private _data;
350
+ private _color;
351
+ private _showRange;
352
+ constructor(label: string, style?: Partial<Style>, opts?: SparklineOptions);
353
+ setData(data: number[]): void;
354
+ pushValue(value: number): void;
355
+ protected _renderSelf(screen: Screen): void;
356
+ }
357
+
358
+ interface StatusIndicatorOptions {
359
+ /** Color when up/active */
360
+ upColor?: Color;
361
+ /** Color when down/inactive */
362
+ downColor?: Color;
363
+ }
364
+ /**
365
+ * StatusIndicator — simple up/down indicator with label.
366
+ *
367
+ * Example:
368
+ * ● API Server — Online
369
+ * ○ Worker — Offline
370
+ */
371
+ declare class StatusIndicator extends Widget {
372
+ private _label;
373
+ private _isUp;
374
+ private _upColor;
375
+ private _downColor;
376
+ constructor(label: string, isUp: boolean, style?: Partial<Style>, opts?: StatusIndicatorOptions);
377
+ setStatus(isUp: boolean): void;
378
+ getStatus(): boolean;
379
+ setLabel(label: string): void;
380
+ protected _renderSelf(screen: Screen): void;
381
+ }
382
+
383
+ interface ProgressBarOptions {
384
+ /** Current value (0–1) */
385
+ value?: number;
386
+ /** Character for the filled portion */
387
+ fillChar?: string;
388
+ /** Character for the empty portion */
389
+ emptyChar?: string;
390
+ /** Color of the filled portion */
391
+ fillColor?: Color;
392
+ /** Show percentage label */
393
+ showLabel?: boolean;
394
+ /** Label format: 'percent' | 'fraction' | 'custom' */
395
+ labelFormat?: 'percent' | 'fraction';
396
+ /** Total for fraction display */
397
+ total?: number;
398
+ }
399
+ /**
400
+ * ProgressBar — horizontal progress indicator.
401
+ *
402
+ * Supports:
403
+ * - Configurable fill/empty characters
404
+ * - Custom fill color
405
+ * - Percentage or fraction label
406
+ * - Smooth animation-ready value changes
407
+ */
408
+ declare class ProgressBar extends Widget {
409
+ private _value;
410
+ private _fillChar;
411
+ private _emptyChar;
412
+ private _fillColor;
413
+ private _showLabel;
414
+ private _labelFormat;
415
+ private _total;
416
+ constructor(style?: Partial<Style>, options?: ProgressBarOptions);
417
+ /** Set progress value (0–1) */
418
+ setValue(value: number): void;
419
+ get value(): number;
420
+ protected _renderSelf(screen: Screen): void;
421
+ }
422
+
423
+ /**
424
+ * Built-in spinner frame sets.
425
+ */
426
+ declare const SPINNER_FRAMES: Record<string, {
427
+ frames: string[];
428
+ interval: number;
429
+ }>;
430
+ interface SpinnerOptions {
431
+ /** Spinner preset name or custom frames */
432
+ spinner?: string | {
433
+ frames: string[];
434
+ interval: number;
435
+ };
436
+ /** Text label displayed after the spinner */
437
+ label?: string;
438
+ /** Color for the spinner frames */
439
+ color?: Color;
440
+ }
441
+ /**
442
+ * Spinner — animated loading indicator.
443
+ *
444
+ * Supports:
445
+ * - 8 built-in spinner presets
446
+ * - Custom frame sequences
447
+ * - Configurable color and label
448
+ * - Automatic frame advancement via tick()
449
+ */
450
+ declare class Spinner extends Widget {
451
+ private _frames;
452
+ private _interval;
453
+ private _frameIndex;
454
+ private _label;
455
+ private _color;
456
+ private _lastTick;
457
+ private _elapsed;
458
+ constructor(style?: Partial<Style>, options?: SpinnerOptions);
459
+ /** Update the spinner label */
460
+ setLabel(label: string): void;
461
+ /**
462
+ * Advance the spinner frame based on elapsed time.
463
+ * Call this with a delta (ms) from the render loop.
464
+ */
465
+ tick(deltaMs: number): void;
466
+ protected _renderSelf(screen: Screen): void;
467
+ }
468
+
469
+ export { Box, Gauge, type GaugeOptions, List, type ListItem, LogView, type LogViewOptions, ProgressBar, type ProgressBarOptions, SPINNER_FRAMES, Sparkline, type SparklineOptions, Spinner, type SpinnerOptions, StatusIndicator, type StatusIndicatorOptions, Table, type TableColumn, type TableOptions, type TableRow, Text, TextInput, type TextProps, Widget, type WidgetEvents };