Skip to content

Commit d36af04

Browse files
committed
Merge branch 'aelzeiny-google-incremental-dom'
2 parents 868ccd1 + a45f432 commit d36af04

File tree

10 files changed

+365
-0
lines changed

10 files changed

+365
-0
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="utf-8" />
6+
<title>Incremental DOM-"keyed"</title>
7+
<link href="/css/currentStyle.css" rel="stylesheet" />
8+
</head>
9+
10+
<body>
11+
<div id='main'></div>
12+
<script src='dist/main.js'></script>
13+
</body>
14+
15+
</html>

frameworks/keyed/incremental-dom/package-lock.json

Lines changed: 77 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"author": "Ahmed Elzeiny",
3+
"license": "Apache-2.0",
4+
"name": "js-framework-benchmark-incremental-dom",
5+
"version": "1.0.0",
6+
"description": "Benchmark for Google's incremental-dom framework",
7+
"js-framework-benchmark": {
8+
"frameworkVersionFromPackage": "incremental-dom",
9+
"frameworkHomeURL": "http://google.github.io/incremental-dom/"
10+
},
11+
"scripts": {
12+
"build-dev": "esbuild src/main.ts --bundle --outfile=dist/main.js --watch",
13+
"build-prod": "esbuild src/main.ts --bundle --minify --outfile=dist/main.js"
14+
},
15+
"keywords": [
16+
"ractive"
17+
],
18+
"homepage": "https://github.com/krausest/js-framework-benchmark",
19+
"repository": {
20+
"type": "git",
21+
"url": "https://github.com/krausest/js-framework-benchmark.git"
22+
},
23+
"dependencies": {
24+
"incremental-dom": "0.7.0"
25+
},
26+
"devDependencies": {
27+
"esbuild": "^0.18.15"
28+
}
29+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { elementOpen, close, text, Key } from 'incremental-dom';
2+
3+
export function Button(id: string, innerText: string, onClick: () => void, key: Key = null) {
4+
elementOpen(
5+
'button', key, null,
6+
// attributes
7+
'class', 'btn btn-primary btn-block',
8+
'id', id
9+
);
10+
text(innerText)
11+
const el = close();
12+
el.addEventListener('click', onClick);
13+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { elementOpen, elementClose } from 'incremental-dom';
2+
3+
export function Div(className: string, func: () => void) {
4+
elementOpen('div', null, null, 'class', className);
5+
func();
6+
elementClose('div');
7+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { elementOpen, close, text } from 'incremental-dom';
2+
3+
export function H1(value: string) {
4+
elementOpen('h1');
5+
text(value);
6+
close();
7+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { elementOpen, elementClose } from "incremental-dom";
2+
3+
export function GlyphIconSpan() {
4+
// <span class="preloadicon glyphicon glyphicon-remove" aria-hidden="true"></span>
5+
6+
elementOpen(
7+
'span', null, null,
8+
// attributes
9+
'class', 'preloadicon glyphicon glyphicon-remove',
10+
'aria-hidden', 'true'
11+
);
12+
13+
elementClose('span');
14+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { elementOpen, elementClose, close, text, open, attr, applyAttrs, applyStatics } from 'incremental-dom';
2+
import { DataEntry } from '../data-collection';
3+
4+
5+
export function Table(func: () => void) {
6+
elementOpen('table', null, null, 'class', 'table table-hover table-striped test-data');
7+
elementOpen('tbody', null, null, 'id', 'tbody');
8+
func();
9+
elementClose('tbody');
10+
elementClose('table');
11+
}
12+
13+
function TableCell(className: string, func: () => void) {
14+
elementOpen('td', null, null, 'class', className);
15+
func();
16+
close();
17+
}
18+
19+
export function TableRow(data: DataEntry, selected: boolean, onRowClick: () => void, onRowDelete: () => void) {
20+
const row = open('tr', data.id);
21+
attr('class', selected ? 'danger' : '');
22+
applyAttrs();
23+
row.onclick = onRowClick;
24+
25+
TableCell('col-md-1', () => text(data.id.toString()));
26+
TableCell('col-md-4', () => {
27+
elementOpen('a');
28+
text(data.label);
29+
close();
30+
});
31+
TableCell('col-md-1', () => {
32+
const deleteEl = elementOpen('a');
33+
deleteEl.onclick = onRowDelete;
34+
elementOpen('span', null, null, 'class', 'glyphicon glyphicon-remove', 'aria-hidden', 'true');
35+
close();
36+
close();
37+
});
38+
TableCell('col-md-6', () => { });
39+
close();
40+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
export interface DataEntry {
2+
label: string;
3+
id: number;
4+
}
5+
6+
const A = ["pretty", "large", "big", "small", "tall", "short", "long", "handsome", "plain", "quaint", "clean",
7+
"elegant", "easy", "angry", "crazy", "helpful", "mushy", "odd", "unsightly", "adorable", "important", "inexpensive",
8+
"cheap", "expensive", "fancy"];
9+
const C = ["red", "yellow", "blue", "green", "pink", "brown", "purple", "brown", "white", "black", "orange"];
10+
const N = ["table", "chair", "house", "bbq", "desk", "car", "pony", "cookie", "sandwich", "burger", "pizza", "mouse",
11+
"keyboard"];
12+
13+
let nextId = 0;
14+
15+
const random = (max) => Math.round(Math.random() * 1000) % max;
16+
17+
export const genLabel = () => `${A[random(A.length)]} ${C[random(C.length)]} ${N[random(N.length)]}`;
18+
export const genId = () => ++nextId;
19+
export function genData(): DataEntry {
20+
return { label: genLabel(), id: genId() }
21+
}
22+
23+
24+
export class DataCollection {
25+
data: DataEntry[];
26+
27+
constructor() {
28+
this.data = [];
29+
}
30+
31+
createRows(n: number) {
32+
this.data = new Array(n);
33+
this.data.length = n;
34+
for (let i = 0; i < n; i++) {
35+
this.data[i] = genData();
36+
}
37+
}
38+
39+
appendRows(n: number) {
40+
for (let i = 0; i < n; i++) {
41+
this.data.push(genData());
42+
}
43+
}
44+
45+
mutate(mutator: (e: DataEntry) => void, increment: number = 1) {
46+
for (let i = 0; i < this.data.length; i += increment) {
47+
mutator(this.data[i]);
48+
}
49+
}
50+
51+
clear() {
52+
this.data = [];
53+
}
54+
55+
swap(idxA: number, idxB: number) {
56+
if (this.data.length > idxA && this.data.length > idxB) {
57+
const tmp = this.data[idxA];
58+
this.data[idxA] = this.data[idxB];
59+
this.data[idxB] = tmp;
60+
}
61+
}
62+
63+
delete(id: number) {
64+
const removeIdx = this.data.findIndex(d => d.id === id);
65+
this.data.splice(removeIdx, 1);
66+
}
67+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/**
2+
* Incremental DOM is a virtual-dom diffing library; not a framework.
3+
* See: http://google.github.io/incremental-dom/
4+
*/
5+
6+
import { patch } from "incremental-dom";
7+
import { DataCollection } from "./data-collection";
8+
9+
import { Button } from "./components/button";
10+
import { H1 } from "./components/header";
11+
import { Div } from "./components/div";
12+
import { GlyphIconSpan } from "./components/span";
13+
import { Table, TableRow } from "./components/table";
14+
15+
16+
let root: HTMLElement;
17+
let state: DataCollection = new DataCollection();
18+
let selectedId: number = null;
19+
20+
function render() {
21+
if (!root) root = document.getElementById('main')!;
22+
patch(root, incrementalDom);
23+
}
24+
25+
function actionRun() {
26+
state.createRows(1000);
27+
render();
28+
}
29+
30+
function actionRunLots() {
31+
state.createRows(10000);
32+
render();
33+
}
34+
35+
function actionAdd() {
36+
state.appendRows(1000);
37+
render();
38+
}
39+
40+
function actionUpdate() {
41+
state.mutate((e) => { e.label += ' !!! ' }, 10);
42+
render();
43+
}
44+
45+
function actionClear() {
46+
state.clear();
47+
render();
48+
}
49+
50+
function actionSwapRows() {
51+
state.swap(1, 998);
52+
render();
53+
}
54+
55+
function actionSelectRow(id: number) {
56+
selectedId = id;
57+
render();
58+
}
59+
60+
function actionDeleteRow(id: number) {
61+
state.delete(id);
62+
render();
63+
}
64+
65+
66+
function incrementalDom() {
67+
const divBtnClass = 'col-sm-6 smallpad';
68+
69+
Div('container', () => {
70+
Div('jumbotron', () => {
71+
Div('row', () => {
72+
Div('col-md-6', () => {
73+
H1('Incremental DOM-"keyed"');
74+
});
75+
Div('col-md-6', () => {
76+
Div(divBtnClass, () => Button('run', 'Create 1,000 rows', actionRun));
77+
Div(divBtnClass, () => Button('runlots', 'Create 10,000 rows', actionRunLots));
78+
Div(divBtnClass, () => Button('add', 'Append 1,000 rows', actionAdd));
79+
Div(divBtnClass, () => Button('update', 'Update every 10th row', actionUpdate));
80+
Div(divBtnClass, () => Button('clear', 'Clear', actionClear));
81+
Div(divBtnClass, () => Button('swaprows', 'Swap Rows', actionSwapRows));
82+
});
83+
});
84+
});
85+
Table(() => {
86+
for (let i = 0; i < state.data.length; i++) {
87+
const id = state.data[i].id;
88+
TableRow(state.data[i], selectedId === id, () => actionSelectRow(id), () => actionDeleteRow(id));
89+
}
90+
});
91+
GlyphIconSpan();
92+
});
93+
94+
}
95+
96+
window.addEventListener('load', render);

0 commit comments

Comments
 (0)