Skip to content

Commit e42c0dc

Browse files
committed
Update ivi to 0.19.0
1 parent e580a98 commit e42c0dc

File tree

2 files changed

+105
-85
lines changed

2 files changed

+105
-85
lines changed

Diff for: frameworks/keyed/ivi/package.json

+5-5
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@
1616
"build-prod": "rollup -c rollup.config.js"
1717
},
1818
"dependencies": {
19-
"ivi-html": "0.16.0",
20-
"ivi-state": "0.16.0",
21-
"ivi": "0.16.0"
19+
"ivi-html": "0.19.0",
20+
"ivi-state": "0.19.0",
21+
"ivi": "0.19.0"
2222
},
2323
"devDependencies": {
24-
"rollup": "0.66.6",
24+
"rollup": "0.67.3",
2525
"rollup-plugin-node-resolve": "3.4.0",
2626
"rollup-plugin-replace": "2.1.0",
2727
"rollup-plugin-terser": "3.0.0"
2828
}
29-
}
29+
}

Diff for: frameworks/keyed/ivi/src/main.js

+100-80
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1-
import { connect, render, map, element, onClick, stopDirtyChecking, setupScheduler, invalidateHandler, invalidate } from "ivi";
1+
import { _, render, Events, onClick, withNextFrame, requestDirtyCheck, elementProto, component, selector, TrackByKey, key } from "ivi";
22
import { h1, div, span, table, tbody, tr, td, a, button } from "ivi-html";
3-
import { createStore, createBox } from "ivi-state";
3+
import { createStore } from "ivi-state";
4+
5+
// @localvoid
6+
// Implemented in almost exactly the same way as react-redux implementation:
7+
// - state is completely immutable
8+
// - each row is a stateful component
9+
// - two selectors per each row (react-redux is using one selector)
410

511
function random(max) {
612
return Math.round(Math.random() * 1000) % max;
@@ -16,7 +22,7 @@ const N = ["table", "chair", "house", "bbq", "desk", "car", "pony", "cookie", "s
1622
let nextId = 1;
1723

1824
function buildData(count) {
19-
const data = new Array(count);
25+
const data = Array(count);
2026
for (let i = 0; i < count; i++) {
2127
data[i] = {
2228
id: nextId++,
@@ -26,100 +32,114 @@ function buildData(count) {
2632
return data;
2733
}
2834

29-
const STORE = createStore(
30-
{ data: createBox([]), selected: 0 },
31-
function (state, action) {
35+
const STORE = createStore({ data: [], selected: 0 },
36+
(state, action) => {
3237
const { data, selected } = state;
33-
const itemList = data.value;
3438
switch (action.type) {
35-
case "delete":
36-
itemList.splice(itemList.findIndex((d) => d.id === action.id), 1);
37-
return { data: createBox(itemList), selected };
39+
case "delete": {
40+
const idx = data.indexOf(action.item);
41+
return { data: [...data.slice(0, idx), ...data.slice(idx + 1)], selected };
42+
}
3843
case "run":
39-
return { data: createBox(buildData(1000)), selected: 0 };
44+
return { data: buildData(1000), selected: 0 };
4045
case "add":
41-
return { data: createBox(itemList.concat(buildData(1000))), selected };
42-
case "update":
43-
for (let i = 0; i < itemList.length; i += 10) {
44-
const r = itemList[i];
45-
itemList[i] = { id: r.id, label: r.label + " !!!" };
46+
return { data: data.concat(buildData(1000)), selected };
47+
case "update": {
48+
const newData = data.slice();
49+
for (let i = 0; i < newData.length; i += 10) {
50+
const r = newData[i];
51+
newData[i] = { id: r.id, label: r.label + " !!!" };
4652
}
47-
return { data, selected };
53+
return { data: newData, selected };
54+
}
4855
case "select":
49-
return { data, selected: action.id };
56+
return { data, selected: action.item.id };
5057
case "runlots":
51-
return { data: createBox(buildData(10000)), selected: 0 };
58+
return { data: buildData(10000), selected: 0 };
5259
case "clear":
53-
return { data: createBox([]), selected: 0 };
60+
return { data: [], selected: 0 };
5461
case "swaprows":
55-
if (itemList.length > 998) {
56-
const a = itemList[1];
57-
itemList[1] = itemList[998];
58-
itemList[998] = a;
59-
}
60-
return { data: createBox(itemList), selected };
62+
return { data: [data[0], data[998], ...data.slice(2, 998), data[1], data[999]], selected };
6163
}
6264
return state;
6365
},
64-
invalidate,
66+
withNextFrame(requestDirtyCheck),
6567
);
6668

67-
const GlyphIcon = element(span("", { "aria-hidden": "true" }));
68-
const RemoveRowButton = element(td("col-md-1").c(a().c(GlyphIcon("glyphicon glyphicon-remove"))));
69+
const useItems = selector(() => STORE.state.data);
70+
const useItem = selector((idx) => STORE.state.data[idx]);
71+
const useSelected = selector((item) => STORE.state.selected === item.id);
6972

70-
const Row = connect(
71-
(_, idx) => {
72-
const state = STORE.state;
73-
const item = state.data.value[idx];
74-
return state.selected === item.id ? { id: item.id, label: item.label, selected: true } : item;
75-
},
76-
(item) => (
77-
stopDirtyChecking(tr(item.selected === true ? "danger" : "").c(
78-
td("col-md-1").t(item.id),
79-
td("col-md-4").c(a().t(item.label)),
80-
RemoveRowButton(),
81-
td("col-md-6"),
82-
))
83-
),
84-
);
73+
const GlyphIcon = elementProto(span("glyphicon glyphicon-remove", { "aria-hidden": "true" }));
74+
const RemoveButton = a(_, _, GlyphIcon());
8575

86-
const RowList = connect(
87-
() => STORE.state.data,
88-
({ value }) => (
89-
tbody().e(onClick((ev) => {
90-
const target = ev.target;
91-
STORE.dispatch({
92-
type: target.matches(".glyphicon") ? "delete" : "select",
93-
id: +target.closest("tr").firstChild.textContent,
94-
});
95-
})).c(map(value, ({ id }, i) => Row(i).k(id)))
96-
),
97-
);
76+
const Row = component((c) => {
77+
let item;
78+
// @localvoid: it is possible to combine multiple selectors into one, like it is traditionally done in react-redux.
79+
// It will slightly improve performance and reduce memory consumption, but I have nothing to hide, selectors are
80+
// super cheap in ivi.
81+
const getItem = useItem(c);
82+
const isSelected = useSelected(c);
9883

99-
function Button(text, id) {
100-
return div("col-sm-6 smallpad").c(
101-
button("btn btn-primary btn-block", { type: "button", id })
102-
.e(onClick(() => { STORE.dispatch({ type: id }); }))
103-
.t(text),
84+
const selectItem = onClick(() => { STORE.dispatch({ type: "select", item }); });
85+
const deleteItem = onClick(() => { STORE.dispatch({ type: "delete", item }); });
86+
87+
return (idx) => (
88+
item = getItem(idx),
89+
90+
tr(isSelected(item) ? "danger" : "", _, [
91+
td("col-md-1", _, item.id),
92+
td("col-md-4", _,
93+
Events(selectItem,
94+
a(_, _, item.label),
95+
),
96+
),
97+
td("col-md-1", _,
98+
Events(deleteItem,
99+
RemoveButton,
100+
),
101+
),
102+
td("col-md-6"),
103+
])
104104
);
105-
}
105+
});
106+
107+
const RowList = component((c) => {
108+
const getItems = useItems(c);
109+
return () => tbody(_, _, TrackByKey(getItems().map(({ id }, i) => key(id, Row(i)))));
110+
});
106111

107-
setupScheduler(invalidateHandler);
108-
render(
109-
div("container").c(
110-
stopDirtyChecking(div("jumbotron").c(div("row").c(
111-
div("col-md-6").c(h1().t("ivi")),
112-
div("col-md-6").c(div("row").c(
113-
Button("Create 1,000 rows", "run"),
114-
Button("Create 10,000 rows", "runlots"),
115-
Button("Append 1,000 rows", "add"),
116-
Button("Update every 10th row", "update"),
117-
Button("Clear", "clear"),
118-
Button("Swap Rows", "swaprows"),
119-
)),
120-
))),
121-
table("table table-hover table-striped test-data").c(RowList()),
122-
GlyphIcon("preloadicon glyphicon glyphicon-remove"),
123-
),
124-
document.getElementById("main"),
112+
const Button = (text, id) => (
113+
div("col-sm-6 smallpad", _,
114+
Events(onClick(() => { STORE.dispatch({ type: id }); }),
115+
button("btn btn-primary btn-block", { type: "button", id }, text),
116+
)
117+
)
125118
);
119+
// `withNextFrame()` runs rendering function inside of a sync frame update tick.
120+
withNextFrame(() => {
121+
render(
122+
div("container", _, [
123+
div("jumbotron", _,
124+
div("row", _, [
125+
div("col-md-6", _,
126+
h1(_, _, "ivi")
127+
),
128+
div("col-md-6", _,
129+
div("row", _, [
130+
Button("Create 1,000 rows", "run"),
131+
Button("Create 10,000 rows", "runlots"),
132+
Button("Append 1,000 rows", "add"),
133+
Button("Update every 10th row", "update"),
134+
Button("Clear", "clear"),
135+
Button("Swap Rows", "swaprows"),
136+
]),
137+
),
138+
]),
139+
),
140+
table("table table-hover table-striped test-data", _, RowList()),
141+
GlyphIcon("preloadicon glyphicon glyphicon-remove")
142+
]),
143+
document.getElementById("main"),
144+
);
145+
})();

0 commit comments

Comments
 (0)