Skip to content

Commit

Permalink
Example: Adding a very basic version of a todo app (#311)
Browse files Browse the repository at this point in the history
* adding todoapp

* reformating

* Fixing the build issue
  • Loading branch information
faisalil authored and bryphe committed Feb 9, 2019
1 parent c18eaa7 commit 47e7b19
Show file tree
Hide file tree
Showing 4 changed files with 248 additions and 18 deletions.
5 changes: 5 additions & 0 deletions examples/Examples.re
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ let state: state = {
render: _w => AnalogClock.render(),
source: "AnalogClock.re",
},
{
name: "TodoMVC",
render: w => TodoExample.render(w),
source: "TodoExample.re",
},
],
selectedExample: "Animation",
};
Expand Down
3 changes: 0 additions & 3 deletions examples/Hello.re
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,6 @@ module AnimatedText = {

let render = () =>
<View
onMouseWheel={evt =>
print_endline("onMouseWheel: " ++ string_of_float(evt.deltaY))
}
style=Style.[
position(`Absolute),
justifyContent(`Center),
Expand Down
221 changes: 221 additions & 0 deletions examples/TodoExample.re
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
open Revery.UI;
open Revery.Core;
open Revery.UI.Components;

type todo = {
id: int,
task: string,
isDone: bool,
};

module Filter = {
type t =
| All
| Completed
| NotCompleted;

let show = (v: t) =>
switch (v) {
| All => "All"
| Completed => "Completed"
| NotCompleted => "NotCompleted"
};
};

type state = {
todos: list(todo),
filter: Filter.t,
inputValue: string,
nextId: int,
};

type action =
| AddTodo
| ChangeFilter(Filter.t)
| UpdateInputTextValue(string)
| ChangeTaskState(int, bool);

let reducer = (action: action, state: state) => {
switch (action) {
| AddTodo => {
...state,
todos: [
{id: state.nextId, task: state.inputValue, isDone: false},
...state.todos,
],
nextId: state.nextId + 1,
}
| UpdateInputTextValue(text) => {...state, inputValue: text}
| ChangeTaskState(id, isDone) =>
let todos =
List.map(
item => item.id == id ? {...item, isDone} : item,
state.todos,
);
{...state, todos};
| ChangeFilter(filter) => {...state, filter}
};
};

module FilterSection = {
let component = React.component("FilterSection");

let make = (_children, currentFilter, onPickingFilter) =>
component((_slots: React.Hooks.empty) =>
<View
style=Style.[
flexDirection(`Row),
width(500),
alignItems(`Center),
justifyContent(`Center),
]>
<Button
height=50
width=150
fontSize=15
title={Filter.show(Filter.All)}
color={
switch (currentFilter) {
| Filter.All => Colors.dodgerBlue
| _ => Colors.lightSkyBlue
}
}
onClick={() => onPickingFilter(Filter.All)}
/>
<Button
height=50
width=150
fontSize=15
title={Filter.show(Completed)}
color={
switch (currentFilter) {
| Completed => Colors.dodgerBlue
| _ => Colors.lightSkyBlue
}
}
onClick={() => onPickingFilter(Completed)}
/>
<Button
height=50
width=150
fontSize=15
title={Filter.show(NotCompleted)}
color={
switch (currentFilter) {
| NotCompleted => Colors.dodgerBlue
| _ => Colors.lightSkyBlue
}
}
onClick={() => onPickingFilter(NotCompleted)}
/>
</View>
);

let createElement = (~children, ~currentFilter, ~onPickingFilter, ()) =>
React.element(make(children, currentFilter, onPickingFilter));
};

module Example = {
let component = React.component("TodoMVC");

let make = window =>
component(slots => {
let ({todos, inputValue, filter, _}, dispatch, slots) =
React.Hooks.reducer(
~initialState={todos: [], filter: All, inputValue: "", nextId: 0},
reducer,
slots,
);

let _slots: React.Hooks.empty =
React.Hooks.effect(
OnMount,
() => {
let unsubscribe = () => ();
Some(unsubscribe);
},
slots,
);

let renderTodo = task => {
<View style=Style.[flexDirection(`Row)]>
<Checkbox
checked={task.isDone}
onChange={checked => dispatch(ChangeTaskState(task.id, checked))}
/>
<Text
style=Style.[
color(Colors.black),
fontFamily("Roboto-Regular.ttf"),
fontSize(20),
margin(4),
]
text={task.task}
/>
</View>;
};

let filteredList =
List.filter(
task =>
switch (filter) {
| All => true
| Completed => task.isDone
| NotCompleted => !task.isDone
},
todos,
);

let listOfTodos = List.map(renderTodo, filteredList);
<View
style=Style.[
position(`Absolute),
top(0),
bottom(0),
left(0),
right(0),
alignItems(`Center),
justifyContent(`Center),
flexDirection(`Column),
backgroundColor(Colors.white),
]>
<FilterSection
currentFilter=filter
onPickingFilter={filter => dispatch(ChangeFilter(filter))}
/>
<View style=Style.[flexDirection(`Row)]>
<Input
style=Style.[width(400)]
window
placeholder="Add your Todo here"
onChange={(~value) => dispatch(UpdateInputTextValue(value))}
/>
<Button
width=50
height=50
disabled={
switch (inputValue) {
| "" => true
| _ => false
}
}
title="+"
onClick={() => dispatch(AddTodo)}
/>
</View>
<ScrollView
style=Style.[
height(200),
width(450),
border(~width=1, ~color=Colors.black),
]>
<View> ...listOfTodos </View>
</ScrollView>
</View>;
});

let createElement = (~window, ~children as _, ()) =>
React.element(make(window));
};

let render = window => <Example window />;
37 changes: 22 additions & 15 deletions src/UI_Components/Input.re
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ open Revery_Core.Window;

type state = {
value: string,
placeholder: string,
isFocused: bool,
};

Expand All @@ -28,7 +27,8 @@ let reducer = (action, state) =>
switch (action) {
| SetFocus(isFocused) => {...state, isFocused}
| UpdateText(t) =>
state.isFocused ? {...state, value: addCharacter(state.value, t)} : state
state.isFocused
? {isFocused: true, value: addCharacter(state.value, t)} : state
| Backspace =>
state.isFocused
? {
Expand Down Expand Up @@ -67,17 +67,20 @@ let make =
(
~window,
~style,
~value,
~value as valueParam,
~placeholder,
~cursorColor,
~placeholderColor,
~onChange,
(),
) =>
component(slots => {
let initialState = {value, placeholder, isFocused: false};
let (state, dispatch, slots) =
React.Hooks.reducer(~initialState, reducer, slots);
React.Hooks.reducer(
~initialState={value: valueParam, isFocused: false},
reducer,
slots,
);

/*
TODO: Setting the hook to run only on mount means that the onChange
Expand All @@ -87,31 +90,35 @@ let make =
*/
let slots =
React.Hooks.effect(
OnMount,
Always,
() =>
Some(
Event.subscribe(
window.onKeyPress,
event => {
dispatch(UpdateText(event.character));
onChange(~value);
onChange(~value=addCharacter(state.value, event.character));
},
),
),
slots,
);

let handleKeyDown = (~dispatch, event: Events.keyEvent) =>
switch (event.key) {
| Key.KEY_BACKSPACE =>
dispatch(Backspace);
onChange(~value=removeCharacter(state.value));
| _ => ()
};

let slots =
React.Hooks.effect(
OnMount,
Always,
() =>
Some(
Event.subscribe(
window.onKeyDown,
event => {
handleKeyDown(~dispatch, event);
onChange(~value=state.value);
},
Event.subscribe(window.onKeyDown, event =>
handleKeyDown(~dispatch, event)
),
),
slots,
Expand All @@ -132,7 +139,7 @@ let make =

let hasPlaceholder = String.length(state.value) < 1;

let content = hasPlaceholder ? state.placeholder : state.value;
let content = hasPlaceholder ? placeholder : state.value;

/*
computed styles
Expand Down

0 comments on commit 47e7b19

Please sign in to comment.