Add edit form.

This commit is contained in:
Dillon Kearns 2022-08-15 15:13:31 -07:00
parent ef0e421aea
commit 314f15dbf3
2 changed files with 148 additions and 128 deletions

View File

@ -188,6 +188,13 @@ action routeParams =
|> Maybe.withDefault Session.empty |> Maybe.withDefault Session.empty
in in
case formResult of case formResult of
Ok (EditItem itemId) ->
( okSessionThing
-- TODO implement
, Response.render {}
)
|> DataSource.succeed
Ok (DeleteItem itemId) -> Ok (DeleteItem itemId) ->
okSessionThing okSessionThing
|> Session.get "sessionId" |> Session.get "sessionId"
@ -406,14 +413,17 @@ view maybeUrl sharedModel model app =
, body = , body =
[ div [ div
[ class "todomvc-wrapper" [ class "todomvc-wrapper"
, style "visibility" "hidden"
] ]
[ section [ section
[ class "todoapp" ] [ class "todoapp" ]
[ newItemForm [ newItemForm
|> Form.toDynamicFetcher "new-item" |> Form.toDynamicFetcher "new-item"
|> Form.withOnSubmit (\_ -> ClearNewItemInput) |> Form.withOnSubmit (\_ -> ClearNewItemInput)
|> Form.renderHtml [] Nothing app () |> Form.renderHtml
[ class "create-form" ]
Nothing
app
()
, lazy3 viewEntries app app.data.visibility optimisticEntities , lazy3 viewEntries app app.data.visibility optimisticEntities
, lazy2 viewControls app.data.visibility optimisticEntities , lazy2 viewControls app.data.visibility optimisticEntities
] ]
@ -459,6 +469,7 @@ type Action
= CreateItem String = CreateItem String
| DeleteItem String | DeleteItem String
| ToggleItem ( Bool, String ) | ToggleItem ( Bool, String )
| EditItem ( String, String )
completeItemForm : Form.HtmlForm String Action Todo Msg completeItemForm : Form.HtmlForm String Action Todo Msg
@ -547,12 +558,16 @@ viewEntries app visibility entries =
[ class "main" [ class "main"
, style "visibility" cssVisibility , style "visibility" cssVisibility
] ]
[ input [ button
[ class "toggle-all" [ classList
, type_ "checkbox" [ ( "toggle-all", True )
, ( "checked"
, True
)
]
, name "toggle" , name "toggle"
, checked allCompleted
--, checked allCompleted
--, onClick (CheckAll (not allCompleted)) --, onClick (CheckAll (not allCompleted))
] ]
[] []
@ -590,10 +605,12 @@ viewEntry app todo =
Nothing Nothing
app app
todo todo
, label , editItemForm
[--onDoubleClick (EditingEntry todo.id True) |> Form.toDynamicFetcher ("edit-" ++ uuidToString todo.id)
] |> Form.renderHtml []
[ text todo.description ] Nothing
app
todo
, if uuidToString todo.id == "" then , if uuidToString todo.id == "" then
Html.text "" Html.text ""
@ -605,20 +622,39 @@ viewEntry app todo =
app app
todo todo
] ]
, input
[ class "edit"
, value todo.description
, name "title"
, id ("todo-" ++ uuidToString todo.id)
--, onInput (UpdateEntry todo.id)
--, onBlur (EditingEntry todo.id False)
--, onEnter (EditingEntry todo.id False)
]
[]
] ]
editItemForm : Form.HtmlForm String Action Todo Msg
editItemForm =
Form.init
(\itemId description ->
{ combine =
Validation.succeed Tuple.pair
|> Validation.andMap itemId
|> Validation.andMap description
|> Validation.map EditItem
, view =
\formState ->
[ FieldView.input
[ class "edit-input"
, name "title"
, id ("todo-" ++ uuidToString formState.data.id)
]
description
, Html.button [ style "display" "none" ] [ Html.text "Edit" ]
]
}
)
|> Form.hiddenField "itemId" (Field.text |> Field.required "Must be present")
|> Form.field "description"
(Field.text
|> Field.required "Must be present"
|> Field.withInitialValue (.description >> Form.Value.string)
)
|> Form.hiddenKind ( "kind", "edit-item" ) "Expected kind"
uuidToString : Uuid -> String uuidToString : Uuid -> String
uuidToString (Uuid uuid) = uuidToString (Uuid uuid) =
uuid uuid

View File

@ -1,13 +1,11 @@
/* Create to Kent C. Dodds for modified TodoMVC styles: https://github.com/kentcdodds/remix-todomvc/blob/97bbf6c548c71e1535ebe05b707b985f19caf953/app/routes/todos.css */
html, html,
body { body {
margin: 0; margin: 0;
padding: 0; padding: 0;
} }
.todomvc-wrapper {
visibility: visible !important;
}
button { button {
margin: 0; margin: 0;
padding: 0; padding: 0;
@ -18,15 +16,12 @@ button {
font-family: inherit; font-family: inherit;
font-weight: inherit; font-weight: inherit;
color: inherit; color: inherit;
-webkit-appearance: none;
appearance: none;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
font-smoothing: antialiased;
} }
body { body {
font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; font: 14px "Helvetica Neue", Helvetica, Arial, sans-serif;
line-height: 1.4em; line-height: 1.4em;
background: #f5f5f5; background: #f5f5f5;
color: #4d4d4d; color: #4d4d4d;
@ -34,26 +29,15 @@ body {
max-width: 550px; max-width: 550px;
margin: 0 auto; margin: 0 auto;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
font-smoothing: antialiased;
font-weight: 300; font-weight: 300;
} }
button,
input[type="checkbox"] {
outline: none;
}
.hidden {
display: none;
}
.todoapp { .todoapp {
background: #fff; background: #fff;
margin: 130px 0 40px 0; margin: 130px 0 40px 0;
position: relative; position: relative;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
0 25px 50px 0 rgba(0, 0, 0, 0.1);
} }
.todoapp input::-webkit-input-placeholder { .todoapp input::-webkit-input-placeholder {
@ -88,7 +72,7 @@ input[type="checkbox"] {
} }
.new-todo, .new-todo,
.edit { .edit-input {
position: relative; position: relative;
margin: 0; margin: 0;
width: 100%; width: 100%;
@ -96,23 +80,28 @@ input[type="checkbox"] {
font-family: inherit; font-family: inherit;
font-weight: inherit; font-weight: inherit;
line-height: 1.4em; line-height: 1.4em;
border: 0;
outline: none;
color: inherit; color: inherit;
padding: 6px; padding: 6px;
border: 1px solid #999; border: 1px solid #999;
box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
box-sizing: border-box; box-sizing: border-box;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
font-smoothing: antialiased; }
.edit-input {
border: none;
}
.edit-input:not(:focus) {
box-shadow: none;
} }
.new-todo { .new-todo {
padding: 16px 16px 16px 60px; padding: 16px 16px 16px 60px;
border: none; border: none;
background: rgba(0, 0, 0, 0.003); background: rgba(0, 0, 0, 0.003);
box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03); box-shadow: inset 0 -2px 1px rgba(0, 0, 0, 0.03);
} }
.main { .main {
@ -121,28 +110,27 @@ input[type="checkbox"] {
border-top: 1px solid #e6e6e6; border-top: 1px solid #e6e6e6;
} }
label[for='toggle-all'] { .main.no-todos {
display: none; border-top: none;
}
.main.no-todos .toggle-all {
visibility: hidden;
} }
.toggle-all { .toggle-all {
position: absolute;
top: -55px;
left: -12px;
width: 60px; width: 60px;
height: 34px; height: 34px;
text-align: center;
border: none; /* Mobile Safari */
}
.toggle-all:before {
content: '';
font-size: 22px; font-size: 22px;
position: absolute;
top: -52px;
left: 0;
-webkit-transform: rotate(90deg);
transform: rotate(90deg);
color: #e6e6e6; color: #e6e6e6;
padding: 10px 27px 10px 27px;
} }
.toggle-all:checked:before { .toggle-all.checked {
color: #737373; color: #737373;
} }
@ -170,7 +158,7 @@ label[for='toggle-all'] {
.todo-list li.editing .edit { .todo-list li.editing .edit {
display: block; display: block;
width: 506px; width: 506px;
padding: 13px 17px 12px 17px; padding: 12px 16px;
margin: 0 0 0 43px; margin: 0 0 0 43px;
} }
@ -178,49 +166,40 @@ label[for='toggle-all'] {
display: none; display: none;
} }
.todo-list li .toggle { .todo-list li button.toggle {
text-align: center; z-index: 1;
width: 40px; padding: 10px;
/* auto, since non-WebKit browsers doesn't support input styling */
height: auto;
position: absolute; position: absolute;
top: 0; top: 0;
bottom: 0; bottom: 0;
margin: auto 0; margin: auto 0;
border: none; /* Mobile Safari */ display: flex;
-webkit-appearance: none; align-items: center;
appearance: none;
} }
.todo-list li .toggle:after { .todo-list li input {
content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="-10 -18 100 135"><circle cx="50" cy="50" r="50" fill="none" stroke="#ededed" stroke-width="3"/></svg>');
}
.todo-list li .toggle:checked:after {
content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="-10 -18 100 135"><circle cx="50" cy="50" r="50" fill="none" stroke="#bddad5" stroke-width="3"/><path fill="#5dc2af" d="M72 25L42 71 27 56l-4 4 20 20 34-52z"/></svg>');
}
.todo-list li label {
white-space: pre-line;
word-break: break-all; word-break: break-all;
padding: 15px 60px 15px 15px; padding: 15px 15px 15px 60px;
margin-left: 45px;
display: block; display: block;
line-height: 1.2; line-height: 1.2;
transition: color 0.4s; transition: color 0.4s;
} }
.todo-list li.completed label { .todo-list li.completed input {
color: #d9d9d9; color: #d9d9d9;
text-decoration: line-through; text-decoration: line-through;
} }
.todo-list li .destroy { .todo-list li .destroy {
display: none; opacity: 0;
position: absolute; position: absolute;
top: 0; top: 0;
right: 10px; right: 10px;
bottom: 0;
/* this hack makes the "x" look centered */
bottom: -3px;
padding-bottom: 43px;
width: 40px; width: 40px;
height: 40px; height: 40px;
margin: auto 0; margin: auto 0;
@ -230,24 +209,18 @@ label[for='toggle-all'] {
transition: color 0.2s ease-out; transition: color 0.2s ease-out;
} }
.todo-list li .destroy:hover { .todo-list li .destroy:hover,
.todo-list li .destroy:focus {
color: #af5b5e; color: #af5b5e;
} }
.todo-list li .destroy:after { .todo-list li .destroy:after {
content: '×'; content: "×";
} }
.todo-list li:hover .destroy { .todo-list li:hover .destroy,
display: block; .todo-list li .destroy:focus {
} opacity: 1;
.todo-list li .edit {
display: none;
}
.todo-list li.editing:last-child {
margin-bottom: -1px;
} }
.footer { .footer {
@ -259,18 +232,16 @@ label[for='toggle-all'] {
} }
.footer:before { .footer:before {
content: ''; content: "";
position: absolute; position: absolute;
right: 0; right: 0;
bottom: 0; bottom: 0;
left: 0; left: 0;
height: 50px; height: 50px;
overflow: hidden; overflow: hidden;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), 0 8px 0 -3px #f6f6f6,
0 8px 0 -3px #f6f6f6, 0 9px 1px -3px rgba(0, 0, 0, 0.2), 0 16px 0 -6px #f6f6f6,
0 9px 1px -3px rgba(0, 0, 0, 0.2), 0 17px 2px -6px rgba(0, 0, 0, 0.2);
0 16px 0 -6px #f6f6f6,
0 17px 2px -6px rgba(0, 0, 0, 0.2);
} }
.todo-count { .todo-count {
@ -304,7 +275,6 @@ label[for='toggle-all'] {
border-radius: 3px; border-radius: 3px;
} }
.filters li a.selected,
.filters li a:hover { .filters li a:hover {
border-color: rgba(175, 47, 47, 0.1); border-color: rgba(175, 47, 47, 0.1);
} }
@ -320,7 +290,6 @@ html .clear-completed:active {
line-height: 20px; line-height: 20px;
text-decoration: none; text-decoration: none;
cursor: pointer; cursor: pointer;
position: relative;
} }
.clear-completed:hover { .clear-completed:hover {
@ -349,28 +318,6 @@ html .clear-completed:active {
text-decoration: underline; text-decoration: underline;
} }
/*
Hack to remove background from Mobile Safari.
Can't use it globally since it destroys checkboxes in Firefox
*/
@media screen and (-webkit-min-device-pixel-ratio:0) {
.toggle-all,
.todo-list li .toggle {
background: none;
}
.todo-list li .toggle {
height: 40px;
}
.toggle-all {
-webkit-transform: rotate(90deg);
transform: rotate(90deg);
-webkit-appearance: none;
appearance: none;
}
}
@media (max-width: 430px) { @media (max-width: 430px) {
.footer { .footer {
height: 50px; height: 50px;
@ -379,4 +326,41 @@ html .clear-completed:active {
.filters { .filters {
bottom: 10px; bottom: 10px;
} }
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
.error {
color: rgb(185, 28, 28);
}
.create-form {
position: relative;
}
#new-todo-error {
position: absolute;
right: 4px;
bottom: 4px;
}
.update-form {
position: relative;
}
.todo-update-error {
font-size: 14px;
position: absolute;
right: 4px;
bottom: 4px;
} }