or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-widgets.mdcomponents.mdforms.mdicons.mdindex.mdtoolbars.mdutilities.mdwidgets.md
tile.json

advanced-widgets.mddocs/

0

# Advanced Widgets

1

2

Specialized widgets for complex use cases including windowed lists, collapsible panels, search interfaces, and layout components. These widgets handle large datasets, provide advanced interactions, and support complex UI patterns.

3

4

## Capabilities

5

6

### WindowedList

7

8

High-performance widget for rendering large lists with virtual scrolling and dynamic sizing.

9

10

```typescript { .api }

11

/**

12

* Abstract model for windowed list data and behavior

13

*/

14

abstract class WindowedListModel implements WindowedList.IModel {

15

/**

16

* Create windowed list model

17

* @param options - Model configuration options

18

*/

19

constructor(options?: WindowedList.IModelOptions);

20

21

// Abstract methods that must be implemented

22

/** Estimate size of widget at given index */

23

abstract estimateWidgetSize: (index: number) => number;

24

/** Create widget for rendering item at index */

25

abstract widgetRenderer: (index: number) => Widget;

26

27

// Scroll behavior configuration

28

readonly scrollDownThreshold: number = 1;

29

readonly scrollUpThreshold: number = 0;

30

paddingTop: number = 0;

31

32

/** Total height of the viewport */

33

get height(): number;

34

set height(h: number);

35

36

/** Observable list of items */

37

get itemsList(): ISimpleObservableList | null;

38

set itemsList(v: ISimpleObservableList | null);

39

40

/** Number of items to render outside visible area */

41

get overscanCount(): number;

42

set overscanCount(newValue: number);

43

44

/** Current scroll position */

45

get scrollOffset(): number;

46

set scrollOffset(offset: number);

47

48

/** Total number of widgets/items */

49

get widgetCount(): number;

50

set widgetCount(newValue: number);

51

52

/** Whether windowing optimization is active */

53

get windowingActive(): boolean;

54

set windowingActive(newValue: boolean);

55

56

/** Signal emitted when model state changes */

57

get stateChanged(): ISignal<WindowedListModel, IChangedArgs<any, any, string>>;

58

59

// Methods for size and scroll calculations

60

getEstimatedTotalSize(): number;

61

getOffsetForIndexAndAlignment(/* parameters */): number;

62

getRangeToRender(): WindowedList.WindowIndex | null;

63

getSpan(startIndex: number, stopIndex: number): [number, number];

64

resetAfterIndex(index: number): void;

65

setWidgetSize(sizes: { index: number; size: number }[]): boolean;

66

}

67

68

/**

69

* High-performance windowed list widget for large datasets

70

*/

71

class WindowedList<T extends WindowedList.IModel = WindowedList.IModel, U = any> extends Widget {

72

static readonly DEFAULT_WIDGET_SIZE = 50;

73

74

/**

75

* Create windowed list widget

76

* @param options - Widget configuration options

77

*/

78

constructor(options: WindowedList.IOptions<T, U>);

79

80

/** Whether parent container is hidden */

81

get isParentHidden(): boolean;

82

set isParentHidden(v: boolean);

83

84

/** Layout manager for the list */

85

get layout(): WindowedLayout;

86

87

/** Outer container element */

88

get outerNode(): HTMLElement;

89

90

/** Viewport scrolling element */

91

get viewportNode(): HTMLElement;

92

93

/** Whether scrollbar is enabled */

94

get scrollbar(): boolean;

95

set scrollbar(enabled: boolean);

96

97

/**

98

* Scroll to specific offset

99

* @param scrollOffset - Pixel offset to scroll to

100

*/

101

scrollTo(scrollOffset: number): void;

102

103

/**

104

* Scroll to specific item index

105

* @param index - Item index to scroll to

106

* @param align - Alignment behavior

107

* @param margin - Additional margin

108

* @param alignPreference - Alignment preference

109

* @returns Promise that resolves when scroll completes

110

*/

111

scrollToItem(

112

index: number,

113

align?: WindowedList.ScrollToAlign,

114

margin?: number,

115

alignPreference?: WindowedList.BaseScrollToAlignment

116

): Promise<void>;

117

}

118

119

namespace WindowedList {

120

interface IModel<T = any> extends IDisposable {

121

estimateWidgetSize: (index: number) => number;

122

widgetRenderer: (index: number) => Widget;

123

// ... extensive interface with many properties

124

}

125

126

interface IOptions<T extends WindowedList.IModel = WindowedList.IModel, U = any> {

127

/** Model instance */

128

model: T;

129

/** Custom layout */

130

layout?: WindowedLayout;

131

/** Custom renderer */

132

renderer?: IRenderer<U>;

133

/** Enable scrollbar */

134

scrollbar?: boolean;

135

}

136

137

interface IRenderer<T = any> {

138

createOuter(): HTMLElement;

139

createScrollbar(): HTMLElement;

140

createScrollbarViewportIndicator?(): HTMLElement;

141

createScrollbarItem(list: WindowedList, index: number, item: T | undefined): HTMLElement | IRenderer.IScrollbarItem;

142

createViewport(): HTMLElement;

143

}

144

145

type BaseScrollToAlignment = 'center' | 'top-center' | 'start' | 'end';

146

type ScrollToAlign = 'auto' | 'smart' | BaseScrollToAlignment;

147

type WindowIndex = [number, number, number, number];

148

}

149

150

/**

151

* Observable list interface for windowed lists

152

*/

153

interface ISimpleObservableList<T> {

154

readonly length: number;

155

get(index: number): T | undefined;

156

set(index: number, value: T): void;

157

push(...values: T[]): number;

158

insert(index: number, value: T): void;

159

remove(index: number): T | undefined;

160

clear(): void;

161

}

162

```

163

164

**Usage Examples:**

165

166

```typescript

167

import { WindowedList, WindowedListModel, ReactWidget } from '@jupyterlab/ui-components';

168

import { Widget } from '@lumino/widgets';

169

170

// Custom model for file list

171

class FileListModel extends WindowedListModel {

172

constructor(private files: FileInfo[]) {

173

super();

174

this.widgetCount = files.length;

175

}

176

177

estimateWidgetSize = (index: number): number => {

178

// Estimate size based on file type

179

const file = this.files[index];

180

return file?.isDirectory ? 40 : 30;

181

};

182

183

widgetRenderer = (index: number): Widget => {

184

const file = this.files[index];

185

return ReactWidget.create(

186

<div className="file-item">

187

<span className="file-icon">{file.isDirectory ? '๐Ÿ“' : '๐Ÿ“„'}</span>

188

<span className="file-name">{file.name}</span>

189

<span className="file-size">{file.size}</span>

190

</div>

191

);

192

};

193

}

194

195

// Create windowed file list

196

const fileModel = new FileListModel(largeFileArray);

197

const fileList = new WindowedList({

198

model: fileModel,

199

scrollbar: true

200

});

201

202

// Handle large datasets efficiently

203

fileModel.itemsList = new ObservableList(largeFileArray);

204

fileModel.windowingActive = true;

205

fileModel.overscanCount = 10; // Render 10 extra items outside viewport

206

207

// Scroll to specific file

208

async function scrollToFile(fileName: string) {

209

const index = largeFileArray.findIndex(f => f.name === fileName);

210

if (index >= 0) {

211

await fileList.scrollToItem(index, 'center');

212

}

213

}

214

215

// Custom renderer for complex items

216

class CustomFileRenderer implements WindowedList.IRenderer<FileInfo> {

217

createOuter(): HTMLElement {

218

const outer = document.createElement('div');

219

outer.className = 'custom-file-list';

220

return outer;

221

}

222

223

createViewport(): HTMLElement {

224

const viewport = document.createElement('div');

225

viewport.className = 'file-viewport';

226

return viewport;

227

}

228

229

createScrollbar(): HTMLElement {

230

const scrollbar = document.createElement('div');

231

scrollbar.className = 'custom-scrollbar';

232

return scrollbar;

233

}

234

235

createScrollbarItem(list: WindowedList, index: number, file: FileInfo | undefined) {

236

const item = document.createElement('div');

237

item.className = 'scrollbar-item';

238

if (file) {

239

item.textContent = file.name.charAt(0).toUpperCase();

240

}

241

return item;

242

}

243

}

244

```

245

246

### PanelWithToolbar

247

248

Panel widget that combines content area with integrated toolbar.

249

250

```typescript { .api }

251

/**

252

* Panel widget with integrated toolbar

253

*/

254

class PanelWithToolbar extends Panel implements Toolbar.IWidgetToolbar {

255

/**

256

* Create panel with toolbar

257

* @param options - Panel configuration options

258

*/

259

constructor(options?: PanelWithToolbar.IOptions);

260

261

/** Integrated toolbar instance */

262

get toolbar(): Toolbar;

263

}

264

265

namespace PanelWithToolbar {

266

interface IOptions extends Panel.IOptions {

267

/** Custom toolbar instance */

268

toolbar?: Toolbar;

269

}

270

}

271

```

272

273

**Usage Examples:**

274

275

```typescript

276

import { PanelWithToolbar, Toolbar, ToolbarButton } from '@jupyterlab/ui-components';

277

import { saveIcon, refreshIcon } from '@jupyterlab/ui-components';

278

279

// Create panel with integrated toolbar

280

const panel = new PanelWithToolbar();

281

panel.title.label = 'File Editor';

282

panel.addClass('editor-panel');

283

284

// Add buttons to toolbar

285

const saveButton = new ToolbarButton({

286

icon: saveIcon,

287

tooltip: 'Save file',

288

onClick: () => saveCurrentFile()

289

});

290

291

const refreshButton = new ToolbarButton({

292

icon: refreshIcon,

293

tooltip: 'Refresh content',

294

onClick: () => refreshContent()

295

});

296

297

panel.toolbar.addItem('save', saveButton);

298

panel.toolbar.addItem('refresh', refreshButton);

299

300

// Add content widgets

301

const editorWidget = new CodeEditorWrapper();

302

const statusWidget = new StatusBar();

303

304

panel.addWidget(editorWidget);

305

panel.addWidget(statusWidget);

306

307

// Custom toolbar for specific use case

308

const customToolbar = new Toolbar();

309

customToolbar.addClass('custom-editor-toolbar');

310

311

const customPanel = new PanelWithToolbar({

312

toolbar: customToolbar

313

});

314

```

315

316

### SidePanel

317

318

Widget for sidebars with accordion-style layout and toolbar integration.

319

320

```typescript { .api }

321

/**

322

* Widget for sidebars with accordion layout

323

*/

324

class SidePanel extends Widget {

325

/**

326

* Create side panel

327

* @param options - Side panel configuration options

328

*/

329

constructor(options?: SidePanel.IOptions);

330

331

/** Content panel for main widgets */

332

get content(): Panel;

333

334

/** Header panel for titles/controls */

335

get header(): Panel;

336

337

/** Integrated toolbar */

338

get toolbar(): Toolbar;

339

340

/** Array of contained widgets */

341

get widgets(): ReadonlyArray<Widget>;

342

343

/**

344

* Add widget to side panel

345

* @param widget - Widget with toolbar support

346

*/

347

addWidget(widget: Toolbar.IWidgetToolbar): void;

348

349

/**

350

* Insert widget at specific index

351

* @param index - Position to insert at

352

* @param widget - Widget with toolbar support

353

*/

354

insertWidget(index: number, widget: Toolbar.IWidgetToolbar): void;

355

}

356

357

namespace SidePanel {

358

interface IOptions extends AccordionPanel.IOptions {

359

/** Custom content panel */

360

content?: Panel;

361

/** Custom header panel */

362

header?: Panel;

363

/** Custom toolbar */

364

toolbar?: Toolbar;

365

/** Translator for internationalization */

366

translator?: ITranslator;

367

}

368

}

369

```

370

371

**Usage Examples:**

372

373

```typescript

374

import { SidePanel, ReactWidget } from '@jupyterlab/ui-components';

375

376

// Create side panel for file browser

377

const filePanel = new SidePanel();

378

filePanel.title.label = 'Files';

379

filePanel.addClass('file-browser-panel');

380

381

// Create widgets for side panel sections

382

class FileTreeWidget extends ReactWidget {

383

constructor() {

384

super();

385

this.title.label = 'File Tree';

386

this.addClass('file-tree-widget');

387

}

388

389

protected render() {

390

return <div>File tree content here</div>;

391

}

392

}

393

394

class SearchWidget extends ReactWidget {

395

constructor() {

396

super();

397

this.title.label = 'Search';

398

this.addClass('search-widget');

399

}

400

401

protected render() {

402

return <div>Search interface here</div>;

403

}

404

}

405

406

// Add sections to side panel

407

const fileTree = new FileTreeWidget();

408

const searchWidget = new SearchWidget();

409

410

filePanel.addWidget(fileTree);

411

filePanel.addWidget(searchWidget);

412

413

// Access panel components

414

console.log('Content panel:', filePanel.content);

415

console.log('Header panel:', filePanel.header);

416

console.log('Toolbar:', filePanel.toolbar);

417

418

// Add custom toolbar items

419

const refreshButton = new ToolbarButton({

420

icon: refreshIcon,

421

tooltip: 'Refresh files'

422

});

423

filePanel.toolbar.addItem('refresh', refreshButton);

424

```

425

426

### AccordionToolbar

427

428

Specialized accordion panel with toolbar support and custom rendering.

429

430

```typescript { .api }

431

/**

432

* Accordion panel with toolbar support

433

*/

434

namespace AccordionToolbar {

435

class Renderer extends AccordionPanel.Renderer {

436

/**

437

* Create collapse icon for section headers

438

* @param data - Title data for the section

439

* @returns HTML element for collapse icon

440

*/

441

createCollapseIcon(data: Title<Widget>): HTMLElement;

442

443

/**

444

* Create section title element

445

* @param data - Title data for the section

446

* @returns HTML element for section title

447

*/

448

createSectionTitle(data: Title<Widget>): HTMLElement;

449

}

450

451

/** Default renderer instance */

452

const defaultRenderer: Renderer;

453

454

/**

455

* Create layout for accordion with toolbar support

456

* @param options - Accordion panel options

457

* @returns Configured accordion layout

458

*/

459

function createLayout(options: AccordionPanel.IOptions): AccordionLayout;

460

}

461

```

462

463

**Usage Examples:**

464

465

```typescript

466

import { AccordionToolbar, AccordionPanel } from '@jupyterlab/ui-components';

467

468

// Create accordion with custom renderer

469

const accordion = new AccordionPanel({

470

renderer: AccordionToolbar.defaultRenderer

471

});

472

473

// Create layout with toolbar support

474

const layout = AccordionToolbar.createLayout({

475

renderer: AccordionToolbar.defaultRenderer

476

});

477

478

// Custom accordion with enhanced titles

479

class EnhancedAccordion extends AccordionPanel {

480

constructor() {

481

super({

482

renderer: new AccordionToolbar.Renderer()

483

});

484

}

485

}

486

487

const enhancedAccordion = new EnhancedAccordion();

488

489

// Add sections with custom titles

490

const section1 = new ReactWidget();

491

section1.title.label = 'Configuration';

492

section1.title.iconClass = 'jp-SettingsIcon';

493

494

const section2 = new ReactWidget();

495

section2.title.label = 'Advanced Options';

496

section2.title.iconClass = 'jp-AdvancedIcon';

497

498

enhancedAccordion.addWidget(section1);

499

enhancedAccordion.addWidget(section2);

500

```

501

502

### Menu Components

503

504

Enhanced menu system with ranking and disposable items.

505

506

```typescript { .api }

507

/**

508

* Extensible menu interface with ranking support

509

*/

510

interface IRankedMenu extends IDisposable {

511

/**

512

* Add group of menu items with shared rank

513

* @param items - Array of menu item options

514

* @param rank - Rank for ordering (default: 100)

515

* @returns Disposable for removing the group

516

*/

517

addGroup(items: Menu.IItemOptions[], rank?: number): IDisposable;

518

519

/**

520

* Add single menu item with rank

521

* @param options - Menu item options with rank

522

* @returns Disposable menu item

523

*/

524

addItem(options: IRankedMenu.IItemOptions): IDisposable;

525

526

/** Read-only array of menu items */

527

readonly items: ReadonlyArray<Menu.IItem>;

528

529

/** Optional rank for this menu */

530

readonly rank?: number;

531

}

532

533

namespace IRankedMenu {

534

const DEFAULT_RANK = 100;

535

536

interface IItemOptions extends Menu.IItemOptions {

537

/** Item rank for ordering */

538

rank?: number;

539

}

540

541

interface IOptions extends Menu.IOptions {

542

/** Include separators between ranked groups */

543

includeSeparators?: boolean;

544

/** Menu rank */

545

rank?: number;

546

}

547

}

548

549

/**

550

* Menu implementation with ranking support

551

*/

552

class RankedMenu extends Menu implements IRankedMenu {

553

/**

554

* Create ranked menu

555

* @param options - Menu configuration options

556

*/

557

constructor(options: IRankedMenu.IOptions);

558

559

/** Menu rank */

560

get rank(): number | undefined;

561

562

/**

563

* Add group of items with shared rank

564

*/

565

addGroup(items: IRankedMenu.IItemOptions[], rank?: number): IDisposable;

566

567

/**

568

* Add single item with rank

569

*/

570

addItem(options: IRankedMenu.IItemOptions): IDisposableMenuItem;

571

572

/**

573

* Get rank of item at index

574

* @param index - Item index

575

* @returns Item rank

576

*/

577

getRankAt(index: number): number;

578

}

579

580

/**

581

* Disposable menu item interface

582

*/

583

interface IDisposableMenuItem extends IDisposable {

584

// Menu item that can be disposed

585

}

586

```

587

588

**Usage Examples:**

589

590

```typescript

591

import { RankedMenu, IRankedMenu, CommandRegistry } from '@jupyterlab/ui-components';

592

593

// Create ranked menu with commands

594

const commands = new CommandRegistry();

595

596

commands.addCommand('file:new', {

597

label: 'New File',

598

execute: () => console.log('New file')

599

});

600

601

commands.addCommand('file:open', {

602

label: 'Open File',

603

execute: () => console.log('Open file')

604

});

605

606

const fileMenu = new RankedMenu({

607

commands,

608

includeSeparators: true

609

});

610

611

// Add file operations group (high priority)

612

const fileGroup = fileMenu.addGroup([

613

{ command: 'file:new', rank: 10 },

614

{ command: 'file:open', rank: 20 }

615

], 1);

616

617

// Add recent files group (lower priority)

618

const recentGroup = fileMenu.addGroup([

619

{ type: 'submenu', submenu: recentFilesMenu, rank: 10 }

620

], 50);

621

622

// Add individual items

623

const saveItem = fileMenu.addItem({

624

command: 'file:save',

625

rank: 30

626

});

627

628

// Clean up when done

629

function cleanup() {

630

fileGroup.dispose();

631

recentGroup.dispose();

632

saveItem.dispose();

633

}

634

635

// Check item ranks

636

for (let i = 0; i < fileMenu.items.length; i++) {

637

console.log(`Item ${i} rank:`, fileMenu.getRankAt(i));

638

}

639

```

640

641

### Styling Utilities

642

643

Node styling utilities for consistent widget appearance.

644

645

```typescript { .api }

646

/**

647

* Node styling utilities

648

*/

649

namespace Styling {

650

/**

651

* Apply styling to HTML element

652

* @param node - Element to style

653

* @param className - CSS class to apply

654

*/

655

function styleNode(node: HTMLElement, className?: string): void;

656

657

/**

658

* Apply styling to elements by tag name

659

* @param node - Container element

660

* @param tagName - Tag name to target

661

* @param className - CSS class to apply

662

*/

663

function styleNodeByTag(node: HTMLElement, tagName: string, className?: string): void;

664

665

/**

666

* Wrap select element with custom styling

667

* @param node - Select element to wrap

668

* @param multiple - Whether select allows multiple selections

669

* @returns Wrapped container element

670

*/

671

function wrapSelect(node: HTMLSelectElement, multiple?: boolean): HTMLElement;

672

}

673

```

674

675

**Usage Examples:**

676

677

```typescript

678

import { Styling } from '@jupyterlab/ui-components';

679

680

// Style widget nodes

681

class CustomWidget extends Widget {

682

onAfterAttach() {

683

super.onAfterAttach();

684

685

// Apply JupyterLab styling

686

Styling.styleNode(this.node, 'jp-CustomWidget');

687

688

// Style all buttons in widget

689

Styling.styleNodeByTag(this.node, 'button', 'jp-Button');

690

691

// Style all inputs

692

Styling.styleNodeByTag(this.node, 'input', 'jp-InputGroup-input');

693

}

694

}

695

696

// Wrap select elements

697

const selectElement = document.createElement('select');

698

selectElement.innerHTML = `

699

<option value="1">Option 1</option>

700

<option value="2">Option 2</option>

701

`;

702

703

const wrappedSelect = Styling.wrapSelect(selectElement);

704

document.body.appendChild(wrappedSelect);

705

706

// Multi-select wrapper

707

const multiSelect = document.createElement('select');

708

multiSelect.multiple = true;

709

const wrappedMulti = Styling.wrapSelect(multiSelect, true);

710

```