Skip to content

Commit 3ae3ca6

Browse files
Added an unkeyed implementation for Voby
1 parent d12123a commit 3ae3ca6

File tree

4 files changed

+365
-0
lines changed

4 files changed

+365
-0
lines changed

frameworks/non-keyed/voby/index.html

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

frameworks/non-keyed/voby/package-lock.json

Lines changed: 149 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"name": "js-framework-benchmark-voby-unkeyed",
3+
"version": "0.45.0",
4+
"main": "dist/main.js",
5+
"js-framework-benchmark": {
6+
"frameworkVersionFromPackage": "voby",
7+
"frameworkHomeURL": "https://github.com/vobyjs/voby",
8+
"issues": [
9+
1139
10+
]
11+
},
12+
"scripts": {
13+
"build-prod": "rm -rf dist && mkdir dist && esbuild --bundle --minify --target=es2020 --jsx-factory=createElement --jsx-fragment=Fragment --banner:js='\"use strict\";' ./src/main.tsx > ./dist/main.js"
14+
},
15+
"author": "Fabio Spampinato",
16+
"license": "MIT",
17+
"homepage": "https://github.com/krausest/js-framework-benchmark",
18+
"repository": {
19+
"type": "git",
20+
"url": "https://github.com/krausest/js-framework-benchmark.git"
21+
},
22+
"dependencies": {
23+
"voby": "0.48.0"
24+
},
25+
"devDependencies": {
26+
"esbuild": "0.17.16"
27+
}
28+
}
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
2+
/* IMPORT */
3+
4+
import {createElement, Fragment} from 'voby';
5+
import {$, render, template, For} from 'voby';
6+
import type {FunctionMaybe, Observable, ObservableMaybe} from 'voby';
7+
8+
/* TYPES */
9+
10+
type IDatum = {
11+
id: number,
12+
label: Observable<string>
13+
};
14+
15+
/* HELPERS */
16+
17+
const rand = ( max: number ): number => {
18+
return Math.round ( Math.random () * 1000 ) % max;
19+
};
20+
21+
const buildData = (() => {
22+
const adjectives = ['pretty', 'large', 'big', 'small', 'tall', 'short', 'long', 'handsome', 'plain', 'quaint', 'clean', 'elegant', 'easy', 'angry', 'crazy', 'helpful', 'mushy', 'odd', 'unsightly', 'adorable', 'important', 'inexpensive', 'cheap', 'expensive', 'fancy'];
23+
const colors = ['red', 'yellow', 'blue', 'green', 'pink', 'brown', 'purple', 'brown', 'white', 'black', 'orange'];
24+
const nouns = ['table', 'chair', 'house', 'bbq', 'desk', 'car', 'pony', 'cookie', 'sandwich', 'burger', 'pizza', 'mouse', 'keyboard'];
25+
let uuid = 1;
26+
return ( length: number ): IDatum[] => {
27+
const data: IDatum[] = new Array ( length );
28+
for ( let i = 0; i < length; i++ ) {
29+
const id = uuid++;
30+
const adjective = adjectives[rand ( adjectives.length )];
31+
const color = colors[rand ( colors.length )];
32+
const noun = nouns[rand ( nouns.length )];
33+
const label = $(`${adjective} ${color} ${noun}`);
34+
const datum = { id, label };
35+
data[i] = datum;
36+
};
37+
return data;
38+
};
39+
})();
40+
41+
/* MODEL */
42+
43+
const Model = new class {
44+
45+
/* STATE */
46+
47+
data: Observable<IDatum[]> = $<IDatum[]>( [] );
48+
selected: Observable<number> = $( -1 );
49+
50+
/* API */
51+
52+
run0 = (): void => {
53+
this.runWith ( 0 );
54+
};
55+
56+
run1000 = (): void => {
57+
this.runWith ( 1000 );
58+
};
59+
60+
run10000 = (): void => {
61+
this.runWith ( 10000 );
62+
};
63+
64+
runWith = ( length: number ): void => {
65+
this.data ( buildData ( length ) );
66+
};
67+
68+
add = (): void => {
69+
this.data ( data => [...data, ...buildData ( 1000 )] );
70+
};
71+
72+
update = (): void => {
73+
const data = this.data ();
74+
for ( let i = 0, l = data.length; i < l; i += 10 ) {
75+
data[i].label ( label => label + ' !!!' );
76+
}
77+
};
78+
79+
swapRows = (): void => {
80+
const data = this.data ().slice ();
81+
if ( data.length <= 998 ) return;
82+
const datum1 = data[1];
83+
const datum998 = data[998];
84+
data[1] = datum998;
85+
data[998] = datum1;
86+
this.data ( data );
87+
};
88+
89+
remove = ( id: number ): void => {
90+
this.data ( data => {
91+
const idx = data.findIndex ( datum => datum.id === id );
92+
return [...data.slice ( 0, idx ), ...data.slice ( idx + 1 )];
93+
});
94+
};
95+
96+
select = ( id: number ): void => {
97+
this.selected ( id );
98+
};
99+
100+
};
101+
102+
/* COMPONENTS */
103+
104+
const Button = ({ id, text, onClick }: { id: FunctionMaybe<string | number>, text: FunctionMaybe<string>, onClick: ObservableMaybe<(( event: MouseEvent ) => void)> }): JSX.Element => (
105+
<div class="col-sm-6 smallpad">
106+
<button id={id} class="btn btn-primary btn-block" type="button" onClick={onClick}>
107+
{text}
108+
</button>
109+
</div>
110+
);
111+
112+
const Row = template (({ id, label, className, onSelect, onRemove }: { id: FunctionMaybe<string | number>, label: FunctionMaybe<string>, className: FunctionMaybe<Record<string, FunctionMaybe<boolean>>>, onSelect: ObservableMaybe<(( event: MouseEvent ) => void)>, onRemove: ObservableMaybe<(( event: MouseEvent ) => void)> }): JSX.Element => (
113+
<tr class={className}>
114+
<td class="col-md-1">
115+
{id}
116+
</td>
117+
<td class="col-md-4">
118+
<a onClick={onSelect}>
119+
{label}
120+
</a>
121+
</td>
122+
<td class="col-md-1">
123+
<a onClick={onRemove}>
124+
<span class="glyphicon glyphicon-remove" ariaHidden={true} />
125+
</a>
126+
</td>
127+
<td class="col-md-6" />
128+
</tr>
129+
));
130+
131+
const Rows = ({ data }: { data: FunctionMaybe<IDatum[]> }): JSX.Element => (
132+
<For values={data} pooled unkeyed>
133+
{( datum: () => IDatum ) => {
134+
const id = () => datum ().id;
135+
const label = () => datum ().label ();
136+
const selected = () => Model.selected () === id ();
137+
const className = { danger: selected };
138+
const onSelect = () => Model.select ( id () );
139+
const onRemove = () => Model.remove ( id () );
140+
const props = {id, label, className, onSelect, onRemove};
141+
return Row ( props );
142+
}}
143+
</For>
144+
);
145+
146+
const App = (): JSX.Element => (
147+
<div class="container">
148+
<div class="jumbotron">
149+
<div class="row">
150+
<div class="col-md-6">
151+
<h1>Voby</h1>
152+
</div>
153+
<div class="col-md-6">
154+
<div class="row">
155+
<Button id="run" text="Create 1,000 rows" onClick={Model.run1000} />
156+
<Button id="runlots" text="Create 10,000 rows" onClick={Model.run10000} />
157+
<Button id="add" text="Append 1,000 rows" onClick={Model.add} />
158+
<Button id="update" text="Update every 10th row" onClick={Model.update} />
159+
<Button id="clear" text="Clear" onClick={Model.run0} />
160+
<Button id="swaprows" text="Swap Rows" onClick={Model.swapRows} />
161+
</div>
162+
</div>
163+
</div>
164+
</div>
165+
<table class="table table-hover table-striped test-data">
166+
<tbody>
167+
<Rows data={Model.data} />
168+
</tbody>
169+
</table>
170+
<span class="preloadicon glyphicon glyphicon-remove" ariaHidden={true} />
171+
</div>
172+
);
173+
174+
/* RENDER */
175+
176+
render ( <App />, document.getElementById ( 'main' ) );

0 commit comments

Comments
 (0)