open Belt;
let id = ref(0);
let getId = () => {
incr(id);
id.contents;
};
type todo = {
id: int,
text: string,
isComplete: bool,
};
let todoListState = Recoil.atom({key: "todoListState", default: [||]});
type todoListFilter =
| ShowAll
| ShowCompleted
| ShowUncompleted;
let todoListFilterState =
Recoil.atom({key: "todoListFilterState", default: ShowAll});
let filteredTodoListState =
Recoil.selector({
key: "filteredTodoListState",
get: ({get}) => {
let filter = get(todoListFilterState);
let list = get(todoListState);
switch (filter) {
| ShowCompleted => list->Array.keep(item => item.isComplete)
| ShowUncompleted => list->Array.keep(item => !item.isComplete)
| ShowAll => list
};
},
});
type stats = {
totalNum: int,
totalCompletedNum: int,
totalUncompletedNum: int,
percentCompleted: float,
};
let todoListStatsState =
Recoil.selector({
key: "todoListStatsState",
get: ({get}) => {
let todoList = get(filteredTodoListState);
let totalNum = todoList->Array.length;
let totalCompletedNum =
todoList->Array.keep(item => item.isComplete)->Array.length;
let totalUncompletedNum = totalNum - totalCompletedNum;
let percentCompleted =
totalNum === 0
? 0.0 : totalCompletedNum->Float.fromInt /. totalNum->Float.fromInt;
{totalNum, totalCompletedNum, totalUncompletedNum, percentCompleted};
},
});
module TodoItemCreator = {
[@react.component]
let make = () => {
let (inputValue, setInputValue) = React.useState(() => "");
let setTodoList = Recoil.useSetRecoilState(todoListState);
let addItem = () => {
setInputValue(_ => "");
setTodoList(oldTodoList =>
oldTodoList->Array.concat([|
{id: getId(), text: inputValue, isComplete: false},
|])
);
};
let onChange = event => {
let value = event->ReactEvent.Form.target##value;
setInputValue(_ => value);
};
if (event->ReactEvent.Keyboard.key == "Enter") {
addItem();
}
}
/>
;
};
};
module TodoItem = {
let replaceItemAtIndex = (array, index, value) => {
Array.concatMany([|
array->Array.slice(~offset=0, ~len=index),
[|value|],
array->Array.sliceToEnd(index + 1),
|]);
};
let removeItemAtIndex = (array, index) => {
Array.concatMany([|
array->Array.slice(~offset=0, ~len=index),
array->Array.sliceToEnd(index + 1),
|]);
};
[@react.component]
let make = (~item) => {
let (todoList, setTodoList) = Recoil.useRecoilState(todoListState);
let index = todoList->Array.getIndexBy(listItem => listItem === item);
let editItemText = event => {
switch (index) {
| Some(index) =>
let value = event->ReactEvent.Form.target##value;
let newList =
replaceItemAtIndex(todoList, index, {...item, text: value});
setTodoList(_ => newList);
| None => ()
};
};
let toggleItemCompletion = _ => {
switch (index) {
| Some(index) =>
let newList =
replaceItemAtIndex(
todoList,
index,
{...item, isComplete: !item.isComplete},
);
setTodoList(_ => newList);
| None => ()
};
};
let deleteItem = _ => {
switch (index) {
| Some(index) =>
let newList = removeItemAtIndex(todoList, index);
setTodoList(_ => newList);
| None => ()
};
};
;
};
};
module TodoListFilters = {
[@react.component]
let make = () => {
let (filter, setFilter) = Recoil.useRecoilState(todoListFilterState);
let updateFilter = event => {
let value = event->ReactEvent.Form.target##value;
switch (value) {
| "Show All" => setFilter(_ => ShowAll)
| "Show Completed" => setFilter(_ => ShowCompleted)
| "Show Uncompleted" => setFilter(_ => ShowUncompleted)
| _ => ()
};
};
<>
"Filter: "->React.string
>;
};
};
module TodoListStats = {
[@react.component]
let make = () => {
let {totalNum, totalCompletedNum, totalUncompletedNum, percentCompleted} =
Recoil.useRecoilValue(todoListStatsState);
let formattedPercentCompleted = Js.Math.round(percentCompleted *. 100.0);
- {j|Total items: $totalNum|j}->React.string
- {j|Items completed: $totalCompletedNum|j}->React.string
- {j|Items not completed: $totalUncompletedNum|j}->React.string
-
{j|Percent completed: $(formattedPercentCompleted)%|j}->React.string
;
};
};
module TodoList = {
[@react.component]
let make = () => {
let todoList = Recoil.useRecoilValue(filteredTodoListState);
<>
{todoList
->Array.map(({id} as todoItem) =>
)
->React.array}
>;
};
};
ReactDOMRe.renderToElementWithId(
,
"root",
);