or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

app-components.mdblockdom.mdhooks.mdindex.mdlifecycle.mdreactivity.mdtemplates.mdutils-validation.md

blockdom.mddocs/

0

# BlockDOM System

1

2

Virtual DOM implementation with block-based rendering, patching, and lifecycle management optimized for template-driven UIs.

3

4

## Capabilities

5

6

### BlockDOM Object

7

8

The main BlockDOM API object containing core virtual DOM manipulation functions.

9

10

```typescript { .api }

11

/**

12

* BlockDOM API object with core virtual DOM functions

13

*/

14

const blockDom: {

15

/** Configuration object */

16

config: Config;

17

/** Mount a VNode to DOM */

18

mount: (vnode: VNode, fixture: HTMLElement, afterNode?: Node) => void;

19

/** Patch one VNode with another */

20

patch: (vnode1: VNode, vnode2: VNode, withBeforeRemove?: boolean) => void;

21

/** Remove a VNode from DOM */

22

remove: (vnode: VNode, withBeforeRemove?: boolean) => void;

23

/** Create list block */

24

list: (items: any[], template: Function) => VNode;

25

/** Create multi-block container */

26

multi: (blocks: VNode[]) => VNode;

27

/** Create text node */

28

text: (value: string) => VNode;

29

/** Create comment node */

30

comment: (value: string) => VNode;

31

/** Create conditional block */

32

toggler: (condition: boolean, template: Function) => VNode;

33

/** Create generic block */

34

createBlock: (template: Function) => VNode;

35

/** Create raw HTML block */

36

html: (htmlString: string) => VNode;

37

};

38

```

39

40

### Mount Function

41

42

Mounts a virtual node to the DOM.

43

44

```typescript { .api }

45

/**

46

* Mounts a VNode to a DOM element

47

* @param vnode - Virtual node to mount

48

* @param fixture - Target DOM element

49

* @param afterNode - Optional reference node for positioning

50

*/

51

function mount(vnode: VNode, fixture: HTMLElement, afterNode?: Node | null): void;

52

```

53

54

**Usage Examples:**

55

56

```typescript

57

import { blockDom } from "@odoo/owl";

58

59

// Mount a simple text node

60

const textNode = blockDom.text("Hello World");

61

blockDom.mount(textNode, document.body);

62

63

// Mount with positioning

64

const containerDiv = document.getElementById("container");

65

const existingElement = containerDiv.firstChild;

66

const newNode = blockDom.text("Insert before first child");

67

blockDom.mount(newNode, containerDiv, existingElement);

68

69

// Mount complex structures

70

const listNode = blockDom.list([1, 2, 3], (item) =>

71

blockDom.text(`Item: ${item}`)

72

);

73

blockDom.mount(listNode, document.getElementById("list-container"));

74

```

75

76

### Patch Function

77

78

Updates one virtual node with the content of another.

79

80

```typescript { .api }

81

/**

82

* Patches vnode1 with the content of vnode2

83

* @param vnode1 - Target VNode to be updated

84

* @param vnode2 - Source VNode with new content

85

* @param withBeforeRemove - Whether to call beforeRemove hooks

86

*/

87

function patch(vnode1: VNode, vnode2: VNode, withBeforeRemove?: boolean): void;

88

```

89

90

**Usage Examples:**

91

92

```typescript

93

import { blockDom } from "@odoo/owl";

94

95

// Initial render

96

let currentNode = blockDom.text("Loading...");

97

blockDom.mount(currentNode, document.body);

98

99

// Update content

100

const newNode = blockDom.text("Content loaded!");

101

blockDom.patch(currentNode, newNode);

102

currentNode = newNode;

103

104

// Patch lists

105

let currentList = blockDom.list([1, 2, 3], (item) =>

106

blockDom.text(`Item ${item}`)

107

);

108

blockDom.mount(currentList, document.getElementById("list"));

109

110

// Update list with new items

111

const newList = blockDom.list([1, 2, 3, 4, 5], (item) =>

112

blockDom.text(`Updated Item ${item}`)

113

);

114

blockDom.patch(currentList, newList);

115

currentList = newList;

116

117

// Patch with cleanup

118

blockDom.patch(currentNode, newNode, true); // Calls beforeRemove hooks

119

```

120

121

### Remove Function

122

123

Removes a virtual node from the DOM.

124

125

```typescript { .api }

126

/**

127

* Removes a VNode from DOM

128

* @param vnode - Virtual node to remove

129

* @param withBeforeRemove - Whether to call beforeRemove hooks

130

*/

131

function remove(vnode: VNode, withBeforeRemove?: boolean): void;

132

```

133

134

**Usage Examples:**

135

136

```typescript

137

import { blockDom } from "@odoo/owl";

138

139

// Simple removal

140

const node = blockDom.text("Temporary content");

141

blockDom.mount(node, document.body);

142

143

setTimeout(() => {

144

blockDom.remove(node); // Remove after delay

145

}, 2000);

146

147

// Removal with cleanup

148

const complexNode = blockDom.multi([

149

blockDom.text("Part 1"),

150

blockDom.html("<div>Part 2</div>"),

151

blockDom.comment("End of content")

152

]);

153

blockDom.mount(complexNode, document.getElementById("temp-container"));

154

155

// Later remove with beforeRemove hooks

156

blockDom.remove(complexNode, true);

157

```

158

159

### Block Creation Functions

160

161

#### Text Block

162

163

Creates a text node block.

164

165

```typescript { .api }

166

/**

167

* Creates a text node VNode

168

* @param value - Text content

169

* @returns Text VNode

170

*/

171

function text(value: string): VNode;

172

```

173

174

**Usage Examples:**

175

176

```typescript

177

import { blockDom } from "@odoo/owl";

178

179

// Simple text

180

const greeting = blockDom.text("Hello, World!");

181

blockDom.mount(greeting, document.body);

182

183

// Dynamic text content

184

const counter = { value: 0 };

185

const updateCounter = () => {

186

const newText = blockDom.text(`Count: ${counter.value}`);

187

blockDom.patch(currentText, newText);

188

currentText = newText;

189

};

190

191

let currentText = blockDom.text(`Count: ${counter.value}`);

192

blockDom.mount(currentText, document.getElementById("counter"));

193

194

// Update every second

195

setInterval(() => {

196

counter.value++;

197

updateCounter();

198

}, 1000);

199

```

200

201

#### Comment Block

202

203

Creates a comment node block.

204

205

```typescript { .api }

206

/**

207

* Creates a comment node VNode

208

* @param value - Comment text

209

* @returns Comment VNode

210

*/

211

function comment(value: string): VNode;

212

```

213

214

**Usage Examples:**

215

216

```typescript

217

import { blockDom } from "@odoo/owl";

218

219

// Debug comments

220

const debugComment = blockDom.comment("Debug: Component rendered at " + new Date());

221

blockDom.mount(debugComment, document.body);

222

223

// Conditional comments

224

const renderWithComments = (showComments, data) => {

225

const blocks = [];

226

227

if (showComments) {

228

blocks.push(blockDom.comment("Data rendering start"));

229

}

230

231

blocks.push(blockDom.text(JSON.stringify(data)));

232

233

if (showComments) {

234

blocks.push(blockDom.comment("Data rendering end"));

235

}

236

237

return blockDom.multi(blocks);

238

};

239

```

240

241

#### HTML Block

242

243

Creates a block with raw HTML content.

244

245

```typescript { .api }

246

/**

247

* Creates a raw HTML VNode

248

* @param htmlString - Raw HTML string

249

* @returns HTML VNode

250

*/

251

function html(htmlString: string): VNode;

252

```

253

254

**Usage Examples:**

255

256

```typescript

257

import { blockDom } from "@odoo/owl";

258

259

// Simple HTML

260

const richContent = blockDom.html("<p><strong>Bold</strong> and <em>italic</em> text</p>");

261

blockDom.mount(richContent, document.getElementById("content"));

262

263

// HTML from markdown or other sources

264

const markdownHTML = "<h1>Title</h1><p>Some content with <a href='#'>links</a></p>";

265

const markdownBlock = blockDom.html(markdownHTML);

266

blockDom.mount(markdownBlock, document.getElementById("markdown-content"));

267

268

// Dynamic HTML content

269

const renderHTML = (data) => {

270

const html = `

271

<div class="card">

272

<h2>${data.title}</h2>

273

<div class="content">${data.htmlContent}</div>

274

<footer>Created: ${data.createdAt}</footer>

275

</div>

276

`;

277

return blockDom.html(html);

278

};

279

280

const htmlBlock = renderHTML({

281

title: "Article Title",

282

htmlContent: "<p>This is <strong>rich content</strong>.</p>",

283

createdAt: "2024-01-01"

284

});

285

```

286

287

#### List Block

288

289

Creates a list block for rendering arrays of data.

290

291

```typescript { .api }

292

/**

293

* Creates a list VNode for rendering arrays

294

* @param items - Array of items to render

295

* @param template - Function that creates VNode for each item

296

* @returns List VNode

297

*/

298

function list<T>(items: T[], template: (item: T, index: number) => VNode): VNode;

299

```

300

301

**Usage Examples:**

302

303

```typescript

304

import { blockDom } from "@odoo/owl";

305

306

// Simple list

307

const numbers = [1, 2, 3, 4, 5];

308

const numberList = blockDom.list(numbers, (num, index) =>

309

blockDom.text(`${index + 1}. ${num}`)

310

);

311

blockDom.mount(numberList, document.getElementById("numbers"));

312

313

// Complex object list

314

const users = [

315

{ id: 1, name: "Alice", email: "alice@example.com" },

316

{ id: 2, name: "Bob", email: "bob@example.com" },

317

{ id: 3, name: "Charlie", email: "charlie@example.com" }

318

];

319

320

const userList = blockDom.list(users, (user) => {

321

const userHTML = `

322

<div class="user-card" data-user-id="${user.id}">

323

<h3>${user.name}</h3>

324

<p>${user.email}</p>

325

</div>

326

`;

327

return blockDom.html(userHTML);

328

});

329

330

blockDom.mount(userList, document.getElementById("user-list"));

331

332

// Nested lists

333

const categories = [

334

{

335

name: "Fruits",

336

items: ["Apple", "Banana", "Orange"]

337

},

338

{

339

name: "Vegetables",

340

items: ["Carrot", "Broccoli", "Spinach"]

341

}

342

];

343

344

const categoryList = blockDom.list(categories, (category) => {

345

const categoryHeader = blockDom.html(`<h2>${category.name}</h2>`);

346

const itemList = blockDom.list(category.items, (item) =>

347

blockDom.html(`<li>${item}</li>`)

348

);

349

350

return blockDom.multi([

351

categoryHeader,

352

blockDom.html("<ul>"),

353

itemList,

354

blockDom.html("</ul>")

355

]);

356

});

357

```

358

359

#### Multi Block

360

361

Creates a container for multiple blocks.

362

363

```typescript { .api }

364

/**

365

* Creates a multi-block container

366

* @param blocks - Array of VNodes to group together

367

* @returns Multi VNode containing all blocks

368

*/

369

function multi(blocks: VNode[]): VNode;

370

```

371

372

**Usage Examples:**

373

374

```typescript

375

import { blockDom } from "@odoo/owl";

376

377

// Group related elements

378

const header = blockDom.html("<h1>Welcome</h1>");

379

const content = blockDom.text("This is the main content.");

380

const footer = blockDom.html("<footer>&copy; 2024</footer>");

381

382

const page = blockDom.multi([header, content, footer]);

383

blockDom.mount(page, document.body);

384

385

// Conditional multi-blocks

386

const renderPage = (user, showHeader = true, showFooter = true) => {

387

const blocks = [];

388

389

if (showHeader) {

390

blocks.push(blockDom.html(`<header><h1>Welcome, ${user.name}!</h1></header>`));

391

}

392

393

blocks.push(blockDom.html(`

394

<main>

395

<p>User ID: ${user.id}</p>

396

<p>Email: ${user.email}</p>

397

</main>

398

`));

399

400

if (showFooter) {

401

blocks.push(blockDom.html("<footer>Thank you for visiting!</footer>"));

402

}

403

404

return blockDom.multi(blocks);

405

};

406

407

// Dynamic multi-blocks

408

const renderDashboard = (widgets) => {

409

const blocks = [

410

blockDom.html("<div class='dashboard-header'><h1>Dashboard</h1></div>")

411

];

412

413

widgets.forEach(widget => {

414

blocks.push(blockDom.html(`

415

<div class="widget" data-widget-id="${widget.id}">

416

<h2>${widget.title}</h2>

417

<div class="widget-content">${widget.content}</div>

418

</div>

419

`));

420

});

421

422

blocks.push(blockDom.html("<div class='dashboard-footer'>Last updated: " + new Date().toLocaleString() + "</div>"));

423

424

return blockDom.multi(blocks);

425

};

426

```

427

428

#### Toggler Block

429

430

Creates a conditional block that shows/hides content.

431

432

```typescript { .api }

433

/**

434

* Creates a conditional block

435

* @param condition - Boolean condition for showing content

436

* @param template - Function that creates VNode when condition is true

437

* @returns Toggler VNode

438

*/

439

function toggler(condition: boolean, template: () => VNode): VNode;

440

```

441

442

**Usage Examples:**

443

444

```typescript

445

import { blockDom } from "@odoo/owl";

446

447

// Simple conditional rendering

448

let showMessage = false;

449

const messageToggler = blockDom.toggler(showMessage, () =>

450

blockDom.text("This message is conditionally shown!")

451

);

452

blockDom.mount(messageToggler, document.getElementById("message-area"));

453

454

// Toggle visibility

455

document.getElementById("toggle-button").onclick = () => {

456

showMessage = !showMessage;

457

const newToggler = blockDom.toggler(showMessage, () =>

458

blockDom.text("This message is conditionally shown!")

459

);

460

blockDom.patch(messageToggler, newToggler);

461

};

462

463

// Complex conditional content

464

const user = { isAdmin: false, name: "John" };

465

const adminPanel = blockDom.toggler(user.isAdmin, () => {

466

const adminContent = `

467

<div class="admin-panel">

468

<h2>Admin Panel</h2>

469

<button>Manage Users</button>

470

<button>System Settings</button>

471

<button>View Logs</button>

472

</div>

473

`;

474

return blockDom.html(adminContent);

475

});

476

477

// Loading states

478

let isLoading = true;

479

const loadingToggler = blockDom.toggler(isLoading, () =>

480

blockDom.html("<div class='spinner'>Loading...</div>")

481

);

482

483

const content = blockDom.multi([

484

loadingToggler,

485

blockDom.toggler(!isLoading, () =>

486

blockDom.html("<div>Content loaded successfully!</div>")

487

)

488

]);

489

490

// Simulate loading completion

491

setTimeout(() => {

492

isLoading = false;

493

// Re-render with updated condition

494

}, 2000);

495

```

496

497

#### Create Block

498

499

Creates a generic block from a template function.

500

501

```typescript { .api }

502

/**

503

* Creates a generic block from template function

504

* @param template - Function that generates DOM structure

505

* @returns Generic block VNode

506

*/

507

function createBlock(template: () => HTMLElement | string): VNode;

508

```

509

510

**Usage Examples:**

511

512

```typescript

513

import { blockDom } from "@odoo/owl";

514

515

// Create custom block

516

const customBlock = blockDom.createBlock(() => {

517

const div = document.createElement("div");

518

div.className = "custom-widget";

519

div.innerHTML = "<p>Custom widget content</p>";

520

521

// Add event listeners

522

div.addEventListener("click", () => {

523

console.log("Custom widget clicked!");

524

});

525

526

return div;

527

});

528

529

blockDom.mount(customBlock, document.getElementById("widget-container"));

530

531

// Block with dynamic content

532

const dynamicBlock = (data) => blockDom.createBlock(() => {

533

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

534

container.className = "dynamic-content";

535

536

// Create elements programmatically

537

const title = document.createElement("h2");

538

title.textContent = data.title;

539

container.appendChild(title);

540

541

const list = document.createElement("ul");

542

data.items.forEach(item => {

543

const listItem = document.createElement("li");

544

listItem.textContent = item;

545

list.appendChild(listItem);

546

});

547

container.appendChild(list);

548

549

return container;

550

});

551

552

// Chart integration

553

const chartBlock = (chartData) => blockDom.createBlock(() => {

554

const canvas = document.createElement("canvas");

555

canvas.width = 400;

556

canvas.height = 300;

557

558

// Initialize chart library

559

const ctx = canvas.getContext("2d");

560

new ThirdPartyChart(ctx, chartData);

561

562

return canvas;

563

});

564

```

565

566

### VNode Interface

567

568

The core virtual node interface that all BlockDOM elements implement.

569

570

```typescript { .api }

571

/**

572

* Virtual node interface implemented by all BlockDOM elements

573

*/

574

interface VNode<T = any> {

575

/** Mounts the VNode to a parent element */

576

mount(parent: HTMLElement, afterNode: Node | null): void;

577

/** Moves the VNode before a DOM node */

578

moveBeforeDOMNode(node: Node | null, parent?: HTMLElement): void;

579

/** Moves the VNode before another VNode */

580

moveBeforeVNode(other: T | null, afterNode: Node | null): void;

581

/** Patches this VNode with another VNode */

582

patch(other: T, withBeforeRemove: boolean): void;

583

/** Called before removal for cleanup */

584

beforeRemove(): void;

585

/** Removes the VNode from DOM */

586

remove(): void;

587

/** Gets the first DOM node of this VNode */

588

firstNode(): Node | undefined;

589

/** Associated DOM element (if any) */

590

el?: HTMLElement | Text;

591

/** Parent DOM element */

592

parentEl?: HTMLElement;

593

/** Whether this is the only child */

594

isOnlyChild?: boolean;

595

/** Unique key for list optimizations */

596

key?: any;

597

}

598

599

/**

600

* Type alias for any BlockDOM VNode

601

*/

602

type BDom = VNode<any>;

603

```