-
Notifications
You must be signed in to change notification settings - Fork 0
/
App.Todo.fs
162 lines (150 loc) · 5.45 KB
/
App.Todo.fs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
module App.Todo
open DeclarativeWPF
open NetAtom
open NetOptics
open System
open System.Windows
open System.Windows.Controls
open System.Windows.Input
type Todo = {Id: int; Completed: bool; Title: string}
module Todo =
let id = Optic.lens (fun t -> t.Id) (fun v t -> {t with Id = v})
let completed =
Optic.lens (fun t -> t.Completed) (fun v t -> {t with Completed = v})
let title = Optic.lens (fun t -> t.Title) (fun v t -> {t with Title = v})
type Filter = All | Active | Completed
module Filter =
let predicate: Filter -> Todo -> bool = function
| All -> fun _ -> true
| Active -> fun t -> not t.Completed
| Completed -> fun t -> t.Completed
type State =
{ NewTodo: string
Todos: IROL<Todo>
Filter: Filter
Editing: option<int> }
module State =
let todos = Optic.lens (fun t -> t.Todos) (fun v t -> {t with Todos = v})
let newTodo =
Optic.lens (fun t -> t.NewTodo) (fun v t -> {t with NewTodo = v})
let filter = Optic.lens (fun t -> t.Filter) (fun v t -> {t with Filter = v})
let editing =
Optic.lens (fun t -> t.Editing) (fun v t -> {t with Editing = v})
module Todos =
let completed = Optic.elemsT << Todo.completed
[<EntryPoint; STAThread>]
let main _ =
let state = Atom.create {
NewTodo = ""
Todos = [|
{Id = 1; Completed = true; Title = "Be functional!"}
{Id = 2; Completed = true; Title = "Be reactive!"}
{Id = 3; Completed = false; Title = "Write cool apps!"}
|]
Filter = All
Editing = None
}
let newTodo = Atom.view State.newTodo state
let todos = Atom.view State.todos state
let filter = Atom.view State.filter state
let editing = Atom.view State.editing state
let allDone =
todos |> Atom.view (Optic.foldLens (Optic.forall id) Todos.completed)
let numLeft =
UI.lift1 (Optic.count (Todos.completed << Optic.whereP not)) todos
let empty = UI.lift1 (fun (xs: IROL<_>) -> xs.Count = 0) todos
let filterButton value =
UI.elem Button [
sprintf "%A" value |> UI.content
UI.onClick <| Atom.setAct filter value
]
UI.run <| Application (
MainWindow = UI.show (
UI.window Window [
UI.title "Todo"
UI.width 300.0
UI.height 300.0
UI.content (
UI.elem StackPanel [
UI.orientation Orientation.Vertical
UI.children [
UI.elem DockPanel [
UI.children [
UI.elem CheckBox [
UI.isChecked allDone
UI.isEnabled <| UI.lift1 not empty
]
UI.elem TextBox [
UI.text newTodo
UI.onEnter <| fun textBox ->
let title = textBox.Text
if title <> "" then
Atom.modify todos
<| fun todos ->
let id =
if todos.Count = 0 then 0
else todos.[todos.Count-1].Id + 1
Optic.set Optic.appendL
[{Todo.Id = id
Todo.Completed = false
Todo.Title = title}]
todos
Atom.set newTodo ""
]
]
]
UI.elem StackPanel [
UI.orientation Orientation.Vertical
todos
|> Atom.view
(UI.lift1
<| fun filter ->
Optic.rewriteI
(Optic.over Optic.arrayI
(Array.sortBy (Optic.view Todo.id)))
<< Optic.filterL (Filter.predicate filter)
<| filter)
|> Atom.mapByKey (Optic.view Todo.id) (fun id todo ->
let editing = Atom.view (Optic.isOrI None (Some id)) editing
UI.elem DockPanel [
UI.children [
UI.elem CheckBox [
UI.isChecked <| Atom.view Todo.completed todo
UI.dock Dock.Left
]
UI.elem Button [
UI.content "Remove"
UI.onClick <| Atom.removeAct todo
UI.dock Dock.Right
]
UI.elem TextBox [
Atom.view Todo.title todo |> UI.text
UI.onLostFocus <| Atom.setAct editing false
UI.onEnter <| fun _ -> Keyboard.ClearFocus()
UI.onMouseDoubleClick <| Atom.setAct editing true
UI.isReadOnly <| UI.lift1 not editing
]
]
])
|> UI.children
]
UI.elem StackPanel [
UI.orientation Orientation.Horizontal
empty.IfElse([], [
UI.elem Label [
UI.lift1 (fun n ->
sprintf "%d item%s left" n (if n = 1 then "" else "s"))
numLeft
|> UI.content
]
filterButton All
filterButton Active
filterButton Completed
]) |> UI.children
]
]
]
)
]
)
)