0
# Lumino Widgets
1
2
Lumino widget classes that provide JupyterLab-compatible components with signals, lifecycle management, and integration with the Lumino widget framework. These widgets can be used in both traditional Lumino applications and modern React-based interfaces.
3
4
## Capabilities
5
6
### ReactWidget Base Class
7
8
Abstract base class for creating Lumino widgets that render React components.
9
10
```typescript { .api }
11
/**
12
* Abstract Lumino widget that renders React components
13
* Bridges React components with Lumino's widget system
14
*/
15
abstract class ReactWidget extends Widget {
16
/**
17
* Create a ReactWidget from a React element
18
* @param element - React element to render
19
* @returns ReactWidget instance
20
*/
21
static create(element: ReactRenderElement): ReactWidget;
22
23
/**
24
* Abstract method to define React content
25
* @returns React element to render or null
26
*/
27
protected abstract render(): ReactRenderElement | null;
28
29
/** Promise that resolves when render is complete */
30
renderPromise?: Promise<void>;
31
}
32
```
33
34
**Usage Examples:**
35
36
```typescript
37
import { ReactWidget } from '@jupyterlab/ui-components';
38
import React from 'react';
39
40
// Custom widget with React content
41
class MyWidget extends ReactWidget {
42
constructor() {
43
super();
44
this.addClass('my-custom-widget');
45
this.title.label = 'My Widget';
46
}
47
48
protected render() {
49
return (
50
<div>
51
<h2>Hello from React!</h2>
52
<button onClick={() => this.handleClick()}>
53
Click me
54
</button>
55
</div>
56
);
57
}
58
59
private handleClick() {
60
console.log('Button clicked in widget');
61
}
62
}
63
64
// Create widget from React element
65
const quickWidget = ReactWidget.create(
66
<div>
67
<p>Quick widget content</p>
68
</div>
69
);
70
```
71
72
### VDomRenderer and VDomModel
73
74
Advanced ReactWidget with model support and automatic re-rendering on model changes.
75
76
```typescript { .api }
77
/**
78
* Abstract ReactWidget with a model that triggers re-renders
79
*/
80
abstract class VDomRenderer<T extends VDomRenderer.IModel | null = null> extends ReactWidget {
81
/**
82
* Create a VDomRenderer with optional model
83
* @param model - Model instance or null
84
*/
85
constructor(model?: T);
86
87
/** Signal emitted when model changes */
88
get modelChanged(): ISignal<this, void>;
89
90
/** Current model instance */
91
set model(newValue: T);
92
get model(): T;
93
}
94
95
namespace VDomRenderer {
96
/**
97
* Interface for VDomRenderer models
98
*/
99
interface IModel extends IDisposable {
100
/** Signal emitted when state changes */
101
readonly stateChanged: ISignal<this, void>;
102
}
103
}
104
105
/**
106
* Concrete implementation of VDomRenderer model
107
*/
108
class VDomModel implements VDomRenderer.IModel {
109
/** Signal for state changes */
110
readonly stateChanged: Signal<this, void>;
111
112
/** Check if model is disposed */
113
get isDisposed(): boolean;
114
115
/** Dispose of the model */
116
dispose(): void;
117
}
118
```
119
120
**Usage Examples:**
121
122
```typescript
123
import { VDomRenderer, VDomModel, ISignal } from '@jupyterlab/ui-components';
124
125
// Custom model for widget data
126
class CounterModel extends VDomModel {
127
constructor() {
128
super();
129
this._count = 0;
130
}
131
132
get count(): number {
133
return this._count;
134
}
135
136
increment(): void {
137
this._count++;
138
this.stateChanged.emit();
139
}
140
141
private _count: number;
142
}
143
144
// Widget that uses the model
145
class CounterWidget extends VDomRenderer<CounterModel> {
146
constructor() {
147
super(new CounterModel());
148
this.addClass('counter-widget');
149
}
150
151
protected render() {
152
return (
153
<div>
154
<p>Count: {this.model.count}</p>
155
<button onClick={() => this.model.increment()}>
156
Increment
157
</button>
158
</div>
159
);
160
}
161
}
162
163
// Widget automatically re-renders when model changes
164
const counter = new CounterWidget();
165
```
166
167
### UseSignal React Component
168
169
React component for connecting to Lumino signals within React components.
170
171
```typescript { .api }
172
/**
173
* React component for listening to Lumino signals
174
*/
175
interface IUseSignalProps<SENDER, ARGS> {
176
/** Signal to listen to */
177
signal: ISignal<SENDER, ARGS>;
178
/** Initial sender value */
179
initialSender?: SENDER;
180
/** Initial arguments value */
181
initialArgs?: ARGS;
182
/** Render function called with signal data */
183
children: (sender?: SENDER, args?: ARGS) => React.ReactNode;
184
/** Function to determine if component should update */
185
shouldUpdate?: (sender: SENDER, args: ARGS) => boolean;
186
}
187
188
class UseSignal<SENDER, ARGS> extends React.Component<
189
IUseSignalProps<SENDER, ARGS>,
190
IUseSignalState<SENDER, ARGS>
191
>;
192
```
193
194
**Usage Examples:**
195
196
```typescript
197
import React from 'react';
198
import { UseSignal, ReactWidget } from '@jupyterlab/ui-components';
199
import { Signal } from '@lumino/signaling';
200
201
// Widget with signal
202
class DataWidget extends ReactWidget {
203
constructor() {
204
super();
205
this._dataChanged = new Signal(this);
206
}
207
208
get dataChanged(): ISignal<this, string[]> {
209
return this._dataChanged;
210
}
211
212
updateData(newData: string[]): void {
213
this._dataChanged.emit(newData);
214
}
215
216
protected render() {
217
return (
218
<div>
219
<UseSignal signal={this.dataChanged}>
220
{(sender, data) => (
221
<ul>
222
{data?.map((item, i) => (
223
<li key={i}>{item}</li>
224
))}
225
</ul>
226
)}
227
</UseSignal>
228
</div>
229
);
230
}
231
232
private _dataChanged: Signal<this, string[]>;
233
}
234
235
// Usage with shouldUpdate
236
<UseSignal
237
signal={mySignal}
238
shouldUpdate={(sender, args) => args.length > 0}
239
>
240
{(sender, args) => <div>Items: {args?.length}</div>}
241
</UseSignal>
242
```
243
244
### Switch Widget
245
246
Toggle switch widget with value tracking and change signals.
247
248
```typescript { .api }
249
/**
250
* Switch widget for boolean toggle functionality
251
*/
252
class Switch extends Widget {
253
constructor();
254
255
/** Current switch value */
256
get value(): boolean;
257
set value(newValue: boolean);
258
259
/** Signal emitted when value changes */
260
get valueChanged(): ISignal<this, IChangedArgs<boolean, boolean, 'value'>>;
261
262
/** Switch label text */
263
get label(): string;
264
set label(x: string);
265
266
/** Switch caption text */
267
get caption(): string;
268
set caption(x: string);
269
}
270
```
271
272
**Usage Examples:**
273
274
```typescript
275
import { Switch } from '@jupyterlab/ui-components';
276
277
// Basic switch usage
278
const debugSwitch = new Switch();
279
debugSwitch.label = 'Enable Debug Mode';
280
debugSwitch.caption = 'Shows additional debugging information';
281
debugSwitch.value = false;
282
283
// Listen for changes
284
debugSwitch.valueChanged.connect((sender, args) => {
285
console.log(`Debug mode ${args.newValue ? 'enabled' : 'disabled'}`);
286
updateDebugMode(args.newValue);
287
});
288
289
// Programmatically toggle
290
function toggleDebug() {
291
debugSwitch.value = !debugSwitch.value;
292
}
293
294
// Add to layout
295
const panel = new Panel();
296
panel.addWidget(debugSwitch);
297
```
298
299
### Collapser Widget
300
301
Collapsible container widget for expandable content sections.
302
303
```typescript { .api }
304
/**
305
* Collapsible panel widget that can contain other widgets
306
*/
307
class Collapser<T extends Widget = Widget> extends Widget {
308
/**
309
* Create a Collapser widget
310
* @param options - Configuration options
311
*/
312
constructor(options: Collapser.IOptions<T>);
313
314
/** The contained widget */
315
get widget(): T;
316
set widget(widget: T);
317
318
/** Whether the collapser is collapsed */
319
get collapsed(): boolean;
320
set collapsed(value: boolean);
321
322
/** Signal emitted when collapse state changes */
323
get collapseChanged(): ISignal<Collapser, void>;
324
325
/** Toggle the collapse state */
326
toggle(): void;
327
328
/** Dispose of the widget and its contents */
329
dispose(): void;
330
}
331
332
namespace Collapser {
333
interface IOptions<T extends Widget = Widget> extends Widget.IOptions {
334
/** Widget to contain */
335
widget: T;
336
/** Initial collapsed state */
337
collapsed?: boolean;
338
}
339
}
340
```
341
342
**Usage Examples:**
343
344
```typescript
345
import { Collapser, ReactWidget } from '@jupyterlab/ui-components';
346
347
// Create content widget
348
const contentWidget = ReactWidget.create(
349
<div>
350
<h3>Expandable Content</h3>
351
<p>This content can be collapsed.</p>
352
</div>
353
);
354
355
// Create collapser
356
const collapser = new Collapser({
357
widget: contentWidget,
358
collapsed: true
359
});
360
361
// Listen for collapse changes
362
collapser.collapseChanged.connect(() => {
363
console.log(`Panel ${collapser.collapsed ? 'collapsed' : 'expanded'}`);
364
});
365
366
// Control programmatically
367
function expandPanel() {
368
collapser.collapsed = false;
369
}
370
371
function collapsePanel() {
372
collapser.collapsed = true;
373
}
374
375
// Toggle with animation
376
function animatedToggle() {
377
collapser.toggle();
378
}
379
```
380
381
### Spinner Widget
382
383
Loading spinner widget for indicating progress.
384
385
```typescript { .api }
386
/**
387
* Spinner widget for showing loading states
388
*/
389
class Spinner extends Widget {
390
constructor();
391
}
392
```
393
394
**Usage Examples:**
395
396
```typescript
397
import { Spinner, Panel } from '@jupyterlab/ui-components';
398
399
// Basic spinner
400
const loadingSpinner = new Spinner();
401
loadingSpinner.addClass('my-loading-spinner');
402
403
// Add to layout
404
const container = new Panel();
405
container.addWidget(loadingSpinner);
406
407
// Show/hide spinner based on loading state
408
async function loadData() {
409
container.addWidget(loadingSpinner);
410
411
try {
412
const data = await fetchData();
413
processData(data);
414
} finally {
415
loadingSpinner.parent = null; // Remove from layout
416
}
417
}
418
```
419
420
### IFrame Widget
421
422
Widget that wraps an HTML iframe element with security and loading options.
423
424
```typescript { .api }
425
/**
426
* Lumino widget that wraps an IFrame with security options
427
*/
428
class IFrame extends Widget {
429
/**
430
* Create an IFrame widget
431
* @param options - IFrame configuration options
432
*/
433
constructor(options?: IFrame.IOptions);
434
435
/** Referrer policy for the iframe */
436
get referrerPolicy(): IFrame.ReferrerPolicy;
437
set referrerPolicy(value: IFrame.ReferrerPolicy);
438
439
/** Loading behavior for the iframe */
440
get loading(): IFrame.Loading;
441
set loading(value: IFrame.Loading);
442
443
/** Sandbox restrictions for the iframe */
444
get sandbox(): IFrame.SandboxExceptions[];
445
set sandbox(values: IFrame.SandboxExceptions[]);
446
447
/** URL to load in the iframe */
448
get url(): string;
449
set url(url: string);
450
}
451
452
namespace IFrame {
453
type ReferrerPolicy =
454
| 'no-referrer'
455
| 'no-referrer-when-downgrade'
456
| 'origin'
457
| 'origin-when-cross-origin'
458
| 'same-origin'
459
| 'strict-origin'
460
| 'strict-origin-when-cross-origin'
461
| 'unsafe-url';
462
463
type SandboxExceptions =
464
| 'allow-downloads'
465
| 'allow-forms'
466
| 'allow-modals'
467
| 'allow-orientation-lock'
468
| 'allow-pointer-lock'
469
| 'allow-popups'
470
| 'popups-to-escape-sandbox'
471
| 'allow-presentation'
472
| 'allow-same-origin'
473
| 'allow-scripts'
474
| 'allow-storage-access-by-user-activation'
475
| 'allow-top-navigation'
476
| 'allow-top-navigation-by-user-activation';
477
478
type Loading = 'eager' | 'lazy';
479
480
interface IOptions {
481
/** Sandbox exceptions to allow */
482
sandbox?: SandboxExceptions[];
483
/** Referrer policy */
484
referrerPolicy?: ReferrerPolicy;
485
/** Loading behavior */
486
loading?: Loading;
487
}
488
}
489
```
490
491
**Usage Examples:**
492
493
```typescript
494
import { IFrame } from '@jupyterlab/ui-components';
495
496
// Basic iframe
497
const docViewer = new IFrame({
498
loading: 'lazy',
499
referrerPolicy: 'strict-origin-when-cross-origin'
500
});
501
docViewer.url = 'https://jupyter.org/documentation';
502
503
// Secure iframe with restricted sandbox
504
const secureFrame = new IFrame({
505
sandbox: ['allow-scripts', 'allow-same-origin'],
506
referrerPolicy: 'no-referrer'
507
});
508
secureFrame.url = 'https://trusted-content.example.com';
509
510
// Iframe for user-generated content
511
const userContentFrame = new IFrame({
512
sandbox: [], // No exceptions - maximum security
513
loading: 'lazy'
514
});
515
516
// Update URL dynamically
517
function loadDocument(url: string) {
518
docViewer.url = url;
519
}
520
521
// Modify security settings
522
function allowForms() {
523
const current = secureFrame.sandbox;
524
secureFrame.sandbox = [...current, 'allow-forms'];
525
}
526
```