///
import {
Cell,
computed,
Default,
handler,
NAME,
pattern,
UI,
} from "commontools";
interface RecipeState {
value: Default;
}
// In this case we do not have to type our event parameter because it is not used in the body.
// By requesting a Cell we get a mutable handle when our handler is invoked.
const increment = handler }>((_, state) => {
state.value.set(state.value.get() + 1);
});
// This can also be done with inline types + inference
const decrement = handler((_, state: { value: Cell }) => {
state.value.set(state.value.get() - 1);
});
function previous(value: number) {
return value - 1;
}
function nth(value: number) {
if (value === 1) {
return "1st";
}
if (value === 2) {
return "2nd";
}
if (value === 3) {
return "3rd";
}
return `${value}th`;
}
export const Counter = pattern((state) => {
return {
// computed() is used to create reactive derived values
[NAME]: computed(() => `Simple counter: ${state.value}`),
[UI]: (
{
/* Even though we could end up passing extra data to decrement, our schema prevents that actually reaching the handler.
In fact, we are passing `value` as an OpaqueRef here but it becomes a Cell at invocation time */
}
dec to {previous(state.value)}
{/* transforms pure functions (like nth) into the `derive(c, nth)` equivalent */}
Counter is the {nth(state.value)} number
inc to {state.value + 1}
),
value: state.value,
};
});
export default pattern((state) => {
const counter = Counter({ value: state.value });
return {
[NAME]: computed(() => `Counters: ${state.value}`),
// These three methods are all functionally equivalent
[UI]: (
{counter}
{/* ct-render will NOT usually appear in a recipe, rather, it's used within other ct- component internals */}