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

utilities.mddocs/

0

# Utilities

1

2

Helper functions and utilities for styling, DOM manipulation, positioning, and React integration. These utilities provide common functionality needed when building JupyterLab applications and widgets.

3

4

## Capabilities

5

6

### CSS Class Utilities

7

8

Functions for managing and combining CSS class names in a clean, efficient way.

9

10

```typescript { .api }

11

/**

12

* Combine CSS class names, filtering out falsy values

13

* @param classes - Array of class names, objects, or falsy values

14

* @returns Single combined class string

15

*/

16

function classes(

17

...classes: (string | false | undefined | null | { [className: string]: any })[]

18

): string;

19

20

/**

21

* Combine CSS class names removing duplicates

22

* @param classes - Array of class names, objects, or falsy values

23

* @returns Single combined class string without duplicates

24

*/

25

function classesDedupe(

26

...classes: (string | false | undefined | null | { [className: string]: any })[]

27

): string;

28

```

29

30

**Usage Examples:**

31

32

```typescript

33

import { classes, classesDedupe } from '@jupyterlab/ui-components';

34

35

// Basic class combination

36

const buttonClass = classes(

37

'jp-Button',

38

isActive && 'jp-mod-active',

39

isDisabled && 'jp-mod-disabled',

40

customClass

41

);

42

43

// Conditional classes with objects

44

const widgetClass = classes(

45

'jp-Widget',

46

{

47

'jp-mod-hidden': !visible,

48

'jp-mod-focused': hasFocus,

49

'jp-mod-current': isCurrent

50

},

51

additionalClasses

52

);

53

54

// Remove duplicates when combining from multiple sources

55

const combinedClass = classesDedupe(

56

baseClasses,

57

themeClasses,

58

stateClasses,

59

userClasses

60

);

61

62

// Use in React components

63

function MyButton({ active, disabled, className }: ButtonProps) {

64

return (

65

<button

66

className={classes(

67

'my-button',

68

active && 'active',

69

disabled && 'disabled',

70

className

71

)}

72

>

73

Click me

74

</button>

75

);

76

}

77

78

// Complex conditional styling

79

const iconClass = classes(

80

'icon',

81

size === 'small' && 'icon-small',

82

size === 'large' && 'icon-large',

83

{

84

'icon-spin': spinning,

85

'icon-disabled': !enabled,

86

'icon-primary': variant === 'primary',

87

'icon-secondary': variant === 'secondary'

88

}

89

);

90

```

91

92

### DOM Utilities

93

94

Functions for working with DOM elements and converting between DOM and React patterns.

95

96

```typescript { .api }

97

/**

98

* Convert DOM element attributes to React props

99

* @param elem - DOM element to extract attributes from

100

* @param options - Options for conversion

101

* @returns Object with React-compatible props

102

*/

103

function getReactAttrs(

104

elem: Element,

105

options?: { ignore?: string[] }

106

): { [key: string]: string | null };

107

108

/**

109

* Find tree item element in DOM hierarchy

110

* @param el - Element to start search from

111

* @returns Tree item element or null if not found

112

*/

113

function getTreeItemElement(el: HTMLElement): HTMLElement | null;

114

```

115

116

**Usage Examples:**

117

118

```typescript

119

import { getReactAttrs, getTreeItemElement } from '@jupyterlab/ui-components';

120

121

// Convert DOM element to React props

122

function domElementToReact(element: Element) {

123

const props = getReactAttrs(element, {

124

ignore: ['style', 'class'] // Ignore these attributes

125

});

126

127

return (

128

<div {...props}>

129

Converted from DOM element

130

</div>

131

);

132

}

133

134

// Handle tree interactions

135

function handleTreeClick(event: MouseEvent) {

136

const target = event.target as HTMLElement;

137

const treeItem = getTreeItemElement(target);

138

139

if (treeItem) {

140

const itemId = treeItem.dataset.itemId;

141

console.log('Clicked tree item:', itemId);

142

143

// Handle tree item selection

144

selectTreeItem(itemId);

145

}

146

}

147

148

// Widget that uses DOM utilities

149

class InteractiveWidget extends Widget {

150

onAfterAttach() {

151

super.onAfterAttach();

152

153

// Set up tree interaction handlers

154

this.node.addEventListener('click', (event) => {

155

const treeItem = getTreeItemElement(event.target as HTMLElement);

156

if (treeItem) {

157

this.handleTreeItemClick(treeItem);

158

}

159

});

160

}

161

162

private handleTreeItemClick(element: HTMLElement) {

163

// Convert DOM attributes to work with

164

const attrs = getReactAttrs(element);

165

console.log('Tree item attributes:', attrs);

166

}

167

}

168

169

// Extract data attributes

170

function extractDataAttributes(element: Element) {

171

const attrs = getReactAttrs(element);

172

const dataAttrs: { [key: string]: string } = {};

173

174

Object.entries(attrs).forEach(([key, value]) => {

175

if (key.startsWith('data-') && value !== null) {

176

dataAttrs[key] = value;

177

}

178

});

179

180

return dataAttrs;

181

}

182

```

183

184

### HoverBox Positioning

185

186

Utility for positioning hover elements and tooltips relative to anchor elements.

187

188

```typescript { .api }

189

/**

190

* HoverBox positioning utilities

191

*/

192

type OutOfViewDisplay = 'hidden-inside' | 'hidden-outside' | 'stick-inside' | 'stick-outside';

193

194

namespace HoverBox {

195

interface IOptions {

196

/** Anchor element or position to attach to */

197

anchor: IAnchor;

198

/** Host element that contains the positioned element */

199

host: HTMLElement;

200

/** Maximum height for the positioned element */

201

maxHeight: number;

202

/** Minimum height for the positioned element */

203

minHeight: number;

204

/** Element to be positioned */

205

node: HTMLElement;

206

/** Positioning offsets */

207

offset?: {

208

horizontal?: number;

209

vertical?: { above?: number; below?: number };

210

};

211

/** Vertical positioning preference */

212

privilege?: 'above' | 'below' | 'forceAbove' | 'forceBelow';

213

/** Custom style overrides */

214

style?: CSSStyleDeclaration;

215

/** Behavior when element goes out of view */

216

outOfViewDisplay?: {

217

top?: OutOfViewDisplay;

218

bottom?: OutOfViewDisplay;

219

left?: OutOfViewDisplay;

220

right?: OutOfViewDisplay;

221

};

222

/** Fixed size for the element */

223

size?: { width: number; height: number; };

224

}

225

226

/** Anchor position interface */

227

interface IAnchor extends Pick<DOMRect, 'left' | 'right' | 'top' | 'bottom'> {}

228

229

/**

230

* Set geometry for positioned element

231

* @param options - Positioning configuration

232

*/

233

function setGeometry(options: IOptions): void;

234

}

235

```

236

237

**Usage Examples:**

238

239

```typescript

240

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

241

242

// Create tooltip positioning

243

function createTooltip(anchorElement: HTMLElement, content: string) {

244

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

245

tooltip.className = 'tooltip';

246

tooltip.textContent = content;

247

tooltip.style.position = 'absolute';

248

tooltip.style.zIndex = '1000';

249

250

document.body.appendChild(tooltip);

251

252

// Position tooltip relative to anchor

253

const anchorRect = anchorElement.getBoundingClientRect();

254

255

HoverBox.setGeometry({

256

anchor: {

257

left: anchorRect.left,

258

right: anchorRect.right,

259

top: anchorRect.top,

260

bottom: anchorRect.bottom

261

},

262

host: document.body,

263

node: tooltip,

264

maxHeight: 200,

265

minHeight: 20,

266

privilege: 'above',

267

offset: {

268

vertical: { above: 5, below: 5 },

269

horizontal: 0

270

}

271

});

272

273

return tooltip;

274

}

275

276

// Advanced positioning for dropdown

277

class DropdownWidget extends Widget {

278

private _dropdown: HTMLElement;

279

280

constructor() {

281

super();

282

this._dropdown = this.createDropdown();

283

}

284

285

showDropdown() {

286

const buttonRect = this.node.getBoundingClientRect();

287

288

HoverBox.setGeometry({

289

anchor: {

290

left: buttonRect.left,

291

right: buttonRect.right,

292

top: buttonRect.top,

293

bottom: buttonRect.bottom

294

},

295

host: document.body,

296

node: this._dropdown,

297

maxHeight: 300,

298

minHeight: 50,

299

privilege: 'below',

300

offset: {

301

vertical: { below: 2 }

302

},

303

outOfViewDisplay: {

304

bottom: 'stick',

305

top: 'stick'

306

}

307

});

308

309

this._dropdown.style.display = 'block';

310

}

311

312

hideDropdown() {

313

this._dropdown.style.display = 'none';

314

}

315

316

private createDropdown(): HTMLElement {

317

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

318

dropdown.className = 'dropdown-menu';

319

dropdown.style.position = 'absolute';

320

dropdown.style.display = 'none';

321

dropdown.style.zIndex = '1000';

322

323

document.body.appendChild(dropdown);

324

return dropdown;

325

}

326

}

327

328

// Context menu positioning

329

function showContextMenu(event: MouseEvent, items: MenuItem[]) {

330

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

331

menu.className = 'context-menu';

332

333

items.forEach(item => {

334

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

335

menuItem.className = 'context-menu-item';

336

menuItem.textContent = item.label;

337

menuItem.onclick = item.action;

338

menu.appendChild(menuItem);

339

});

340

341

document.body.appendChild(menu);

342

343

// Position at mouse location

344

HoverBox.setGeometry({

345

anchor: {

346

left: event.clientX,

347

right: event.clientX,

348

top: event.clientY,

349

bottom: event.clientY

350

},

351

host: document.body,

352

node: menu,

353

maxHeight: 400,

354

minHeight: 30,

355

privilege: 'below',

356

outOfViewDisplay: {

357

right: 'stick',

358

bottom: 'stick'

359

}

360

});

361

}

362

```

363

364

### Element Reference Utilities

365

366

Interface for managing element references in React components.

367

368

```typescript { .api }

369

/**

370

* Interface for element reference properties

371

*/

372

interface IElementRefProps<E extends HTMLElement> {

373

/** Callback function to receive element reference */

374

elementRef?: (ref: E | null) => void;

375

}

376

377

/**

378

* Default style class constant

379

*/

380

const DEFAULT_STYLE_CLASS = 'jp-DefaultStyle';

381

```

382

383

**Usage Examples:**

384

385

```typescript

386

import { IElementRefProps, DEFAULT_STYLE_CLASS } from '@jupyterlab/ui-components';

387

388

// Component with element reference

389

interface CustomInputProps extends IElementRefProps<HTMLInputElement> {

390

placeholder?: string;

391

value?: string;

392

onChange?: (value: string) => void;

393

}

394

395

function CustomInput({ elementRef, placeholder, value, onChange }: CustomInputProps) {

396

const handleRef = (element: HTMLInputElement | null) => {

397

if (element) {

398

element.classList.add(DEFAULT_STYLE_CLASS);

399

}

400

elementRef?.(element);

401

};

402

403

return (

404

<input

405

ref={handleRef}

406

placeholder={placeholder}

407

value={value}

408

onChange={(e) => onChange?.(e.target.value)}

409

className="custom-input"

410

/>

411

);

412

}

413

414

// Use with element reference

415

function ParentComponent() {

416

const [inputElement, setInputElement] = useState<HTMLInputElement | null>(null);

417

418

const focusInput = () => {

419

inputElement?.focus();

420

};

421

422

return (

423

<div>

424

<CustomInput

425

elementRef={setInputElement}

426

placeholder="Enter text..."

427

/>

428

<button onClick={focusInput}>Focus Input</button>

429

</div>

430

);

431

}

432

433

// Widget that uses element references

434

class FormWidget extends ReactWidget {

435

private _formElement: HTMLFormElement | null = null;

436

437

protected render() {

438

return (

439

<form

440

ref={(el) => this._formElement = el}

441

className={DEFAULT_STYLE_CLASS}

442

>

443

<CustomInput

444

elementRef={(el) => console.log('Input element:', el)}

445

placeholder="Widget input"

446

/>

447

</form>

448

);

449

}

450

451

submitForm() {

452

if (this._formElement) {

453

this._formElement.requestSubmit();

454

}

455

}

456

}

457

458

// Generic element ref hook pattern

459

function useElementRef<T extends HTMLElement>() {

460

const [element, setElement] = useState<T | null>(null);

461

462

const ref = useCallback((el: T | null) => {

463

if (el) {

464

el.classList.add(DEFAULT_STYLE_CLASS);

465

}

466

setElement(el);

467

}, []);

468

469

return [element, ref] as const;

470

}

471

```

472

473

### Node Styling Utilities

474

475

Utilities for applying consistent styling to DOM elements and their children.

476

477

```typescript { .api }

478

/**

479

* Namespace for DOM node styling utilities

480

*/

481

namespace Styling {

482

/**

483

* Style a node and its child elements with default tag names

484

* @param node - Base DOM element to style

485

* @param className - Optional CSS class to add to styled nodes

486

*/

487

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

488

489

/**

490

* Style a node and elements with a specific tag name

491

* @param node - Base DOM element to style

492

* @param tagName - HTML tag name to target for styling

493

* @param className - Optional CSS class to add to styled nodes

494

*/

495

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

496

497

/**

498

* Wrap select elements with custom styling container

499

* @param select - Select element to wrap

500

* @param multiple - Whether select allows multiple selections

501

*/

502

function wrapSelect(select: HTMLSelectElement, multiple?: boolean): void;

503

}

504

```

505

506

**Usage Examples:**

507

508

```typescript

509

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

510

511

// Style all form elements in a container

512

function styleFormContainer(container: HTMLElement) {

513

// Apply default styling to common form elements

514

Styling.styleNode(container, 'custom-form-style');

515

516

// This will add 'jp-mod-styled' class to all:

517

// - select elements

518

// - textarea elements

519

// - input elements

520

// - button elements

521

}

522

523

// Style specific element types

524

function styleSpecificElements(container: HTMLElement) {

525

// Style only input elements

526

Styling.styleNodeByTag(container, 'input', 'styled-input');

527

528

// Style only buttons

529

Styling.styleNodeByTag(container, 'button', 'styled-button');

530

531

// Style textareas with special class

532

Styling.styleNodeByTag(container, 'textarea', 'styled-textarea');

533

}

534

535

// Widget that applies styling on render

536

class StyledFormWidget extends Widget {

537

onAfterAttach() {

538

super.onAfterAttach();

539

540

// Apply JupyterLab styling to all form elements

541

Styling.styleNode(this.node);

542

}

543

544

addFormField(fieldType: string, className?: string) {

545

const field = document.createElement(fieldType);

546

this.node.appendChild(field);

547

548

// Style the new field specifically

549

Styling.styleNodeByTag(this.node, fieldType, className);

550

}

551

}

552

553

// Custom select wrapper usage

554

function createStyledSelect(options: string[], multiple = false) {

555

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

556

select.multiple = multiple;

557

558

options.forEach(option => {

559

const optionElement = document.createElement('option');

560

optionElement.value = option;

561

optionElement.textContent = option;

562

select.appendChild(optionElement);

563

});

564

565

// Wrap with custom styling - handled automatically by styleNode

566

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

567

container.appendChild(select);

568

Styling.styleNode(container);

569

570

return container;

571

}

572

573

// Apply styling to dynamically created content

574

function createStyledDialog(content: HTMLElement) {

575

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

576

dialog.className = 'dialog-container';

577

dialog.appendChild(content);

578

579

// Ensure all form controls are properly styled

580

Styling.styleNode(dialog, 'dialog-form');

581

582

return dialog;

583

}

584

585

// Utility for styling imported HTML content

586

function styleImportedContent(htmlContent: string) {

587

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

588

container.innerHTML = htmlContent;

589

590

// Style all form elements in the imported content

591

Styling.styleNode(container);

592

593

return container;

594

}

595

```

596

597

### Type Definitions and Constants

598

599

Additional type definitions and constants used throughout the utilities.

600

601

```typescript { .api }

602

// SVG type definitions (from svg.d.ts)

603

declare module '*.svg' {

604

const value: string;

605

export default value;

606

}

607

608

// React render element type

609

type ReactRenderElement = React.ReactElement<any> | null;

610

611

// Common interfaces and types used across utilities

612

interface IChangedArgs<T, U, K extends string> {

613

name: K;

614

oldValue: T;

615

newValue: U;

616

}

617

```

618

619

**Usage Examples:**

620

621

```typescript

622

// Import SVG files as strings

623

import iconSvg from './my-icon.svg';

624

625

// Create LabIcon from imported SVG

626

const myIcon = new LabIcon({

627

name: 'my-app:my-icon',

628

svgstr: iconSvg

629

});

630

631

// Use changed args in signals

632

class CounterModel extends VDomModel {

633

private _count = 0;

634

635

get count(): number {

636

return this._count;

637

}

638

639

set count(value: number) {

640

const oldValue = this._count;

641

this._count = value;

642

643

// Emit change signal with typed args

644

this.stateChanged.emit({

645

name: 'count',

646

oldValue,

647

newValue: value

648

} as IChangedArgs<number, number, 'count'>);

649

}

650

}

651

652

// Utility functions that work with React elements

653

function isValidRenderElement(element: any): element is ReactRenderElement {

654

return element === null || React.isValidElement(element);

655

}

656

657

function renderIfValid(element: ReactRenderElement) {

658

if (isValidRenderElement(element)) {

659

return ReactDOM.render(element, container);

660

}

661

return null;

662

}

663

```