///
import {
Cell,
computed,
Default,
handler,
ID,
lift,
NAME,
recipe,
UI,
} from "commontools";
// NOTE: This example uses [ID] to demonstrate advanced array manipulation features
// and ensure stable references when items are inserted/removed at the front of the array.
// For most basic list patterns, you DON'T need [ID]. See docs/common/PATTERNS.md for
// simpler examples without [ID] that use bidirectional binding.
interface Item {
[ID]: number;
title: string;
}
interface ListInput {
items: Default- ;
}
interface ListOutput extends ListInput {}
const typeTest = handler(
(
_,
state: {
a: Cell
- ;
b: readonly Item[];
c: readonly Cell
- [];
d: Cell
[]>;
},
) => {
const { a, b, c, d } = state;
console.log({ a, b, c, d });
},
);
const resetList = handler((_, state: { items: Cell- }) => {
state.items.set([{ [ID]: 1, title: "A" }, { [ID]: 2, title: "B" }, {
[ID]: 3,
title: "C",
}, { [ID]: 4, title: "D" }]);
});
const deleteFirstItem = handler((_, state: { items: Cell
- }) => {
state.items.set(state.items.get().slice(1));
});
const deleteLastItem = handler((_, state: { items: Cell
- }) => {
const currentItems = state.items.get();
state.items.set(currentItems.slice(0, -1));
});
const deleteAllItems = handler((_, state: { items: Cell
- }) => {
state.items.set([]);
});
const insertItemAtStart = handler((_, state: { items: Cell
- }) => {
const currentItems = state.items.get();
state.items.set([
{ [ID]: Math.random(), title: "New Start" },
...currentItems,
]);
});
const insertItemAtEnd = handler((_, state: { items: Cell
- }) => {
const currentItems = state.items.get();
state.items.set([...currentItems, { [ID]: Math.random(), title: "New End" }]);
});
const shuffleItems = handler((_, state: { items: Cell
- }) => {
const currentItems = state.items.get();
const shuffled = [...currentItems].sort(() => Math.random() - 0.5);
state.items.set(shuffled);
});
const show = lift((items: Item[]) => {
return `${
items.filter((item) => item && item.title).map((item) => item.title).join(
", ",
)
} (${items.length})`;
});
export default recipe(
"list operations",
({ items }) => {
const lowerCase = computed(
() => items.map((item) => item.title.toLowerCase()),
);
// const lowerCase = items.map((item) => item.title.toLowerCase()); // fails with 'TypeError: item.title.toLowerCase is not a function'
// We do not just have top-level support on Cell for the major array operations.
// However, performing them on a ProxyObject (such as within a derive, or calling .get() on a Cell in a handler) will work as expected.
// caveat: behaviour is only guaranteed to be correct for all operations IF the items include an [ID] property.
// excluding the [ID] in this recipe leads to item alignment bugs when insertig or removing from items at the FRONT of an array
const itemsLessThanB = computed(
() => items.filter((item) => item.title < "B"),
);
const extendedItems = computed(
() =>
items.get().concat([
{ [ID]: 5, title: "E" },
{ [ID]: 6, title: "F" },
]),
);
const combinedItems = computed(
() => items.reduce((acc: string, item: Item) => acc += item.title, ""),
);
// Notice that you can bind the same cell to many types
const _x = typeTest({ a: items, b: items, c: items, d: items });
return {
[NAME]: "List demo",
[UI]: (
Reset Demo
Delete First Item
Delete Last Item
Delete All Items
Insert Item at Start
Insert Item at End
Shuffle Items
{show(items)}
{lowerCase}
{show(itemsLessThanB)}
{show(extendedItems)}
{combinedItems}
),
items,
};
},
);
|