///
import {
type Cell,
Default,
derive,
handler,
lift,
recipe,
str,
} from "commontools";
interface EntryDetails {
note: string;
}
interface NestedEntry {
id: string;
label: string;
value: number;
details: EntryDetails;
}
interface NestedGroup {
title: string;
entries: NestedEntry[];
}
interface NestedArrayArgs {
groups: Default;
}
const getEntryValue = (entry: NestedEntry) =>
typeof entry.value === "number" ? entry.value : 0;
interface UpdateNestedEvent {
groupIndex?: number;
entryIndex?: number;
delta?: number;
note?: string;
label?: string;
}
interface AppendEntryEvent {
groupIndex?: number;
label?: string;
note?: string;
value?: number;
}
const updateNestedEntry = handler(
(
event: UpdateNestedEvent | undefined,
context: { groups: Cell },
) => {
if (!event) return;
const groupIndex = typeof event.groupIndex === "number"
? event.groupIndex
: 0;
const entryIndex = typeof event.entryIndex === "number"
? event.entryIndex
: 0;
const groupsValue = context.groups.get();
if (!Array.isArray(groupsValue)) return;
if (groupIndex < 0 || groupIndex >= groupsValue.length) return;
const groupCell = context.groups.key(groupIndex) as Cell;
const entriesCell = groupCell.key("entries") as Cell;
const entriesValue = entriesCell.get();
if (!Array.isArray(entriesValue)) return;
if (entryIndex < 0 || entryIndex >= entriesValue.length) return;
const entryCell = entriesCell.key(entryIndex) as Cell;
const valueCell = entryCell.key("value") as Cell;
const currentValue = valueCell.get() ?? 0;
const delta = typeof event.delta === "number" ? event.delta : 1;
valueCell.set(currentValue + delta);
if (typeof event.label === "string") {
const labelCell = entryCell.key("label") as Cell;
labelCell.set(event.label);
}
if (typeof event.note === "string") {
const detailsCell = entryCell.key("details") as Cell;
const noteCell = detailsCell.key("note") as Cell;
noteCell.set(event.note);
}
},
);
const appendNestedEntry = handler(
(
event: AppendEntryEvent | undefined,
context: { groups: Cell },
) => {
const groupIndex = typeof event?.groupIndex === "number"
? event.groupIndex
: 0;
const groupsValue = context.groups.get();
const groupsArray = Array.isArray(groupsValue) ? groupsValue : [];
if (groupIndex < 0 || groupIndex >= groupsArray.length) return;
const groupCell = context.groups.key(groupIndex) as Cell;
const entriesCell = groupCell.key("entries") as Cell;
const entriesValue = entriesCell.get();
const length = Array.isArray(entriesValue) ? entriesValue.length : 0;
entriesCell.push({
id: `entry-${groupIndex}-${length}`,
label: event?.label ?? `Item ${length + 1}`,
value: typeof event?.value === "number" ? event.value : 0,
details: { note: event?.note ?? "" },
});
},
);
const countTotals = (groups: NestedGroup[] | undefined) => {
const list = Array.isArray(groups) ? groups : [];
return list.reduce((sum, group) => {
const entries = Array.isArray(group.entries) ? group.entries : [];
return sum +
entries.map(getEntryValue).reduce((total, value) => total + value, 0);
}, 0);
};
const summarizeGroups = (groups: NestedGroup[] | undefined) => {
const list = Array.isArray(groups) ? groups : [];
return list.map((group) => {
const entries = Array.isArray(group.entries) ? group.entries : [];
const total = entries
.map(getEntryValue)
.reduce((sum, value) => sum + value, 0);
const notes = entries.map((entry) => entry.details?.note ?? "");
return { title: group.title, total, notes };
});
};
export const counterWithNestedArrayObjects = recipe(
"Counter With Nested Array Objects",
({ groups }) => {
const totals = derive(groups, countTotals);
const summaries = derive(groups, summarizeGroups);
const allNotes = lift((items: { notes: string[] }[] | undefined) => {
const collections = Array.isArray(items) ? items : [];
return collections.flatMap((item) => {
return item.notes.filter((note) =>
typeof note === "string" && note !== ""
);
});
})(summaries);
const headline = str`Nested total ${totals}`;
return {
groups,
totals,
summaries,
headline,
notes: allNotes,
updateEntry: updateNestedEntry({ groups }),
appendEntry: appendNestedEntry({ groups }),
};
},
);