From f1ab8554042dbe7c02a7e53b2a3391378b887308 Mon Sep 17 00:00:00 2001 From: Damyan Ivanov Date: Tue, 15 Feb 2022 21:13:33 +0000 Subject: [PATCH] next step in web-app: adding of lists and list items, marking items as done/pending --- public/css/style.css | 61 +++++++++++++-- public/javascripts/lsl.js | 155 ++++++++++++++++++++++++++++++++++++-- views/index.tt | 14 ++-- 3 files changed, 213 insertions(+), 17 deletions(-) diff --git a/public/css/style.css b/public/css/style.css index 80f1435..98ef565 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -5,12 +5,12 @@ margin-bottom: 25px; padding: 0; background-color: #ddd; font-family: "Bitstream Vera Sans", sans-serif; -font-size: 12pt; +font-size: 14pt; color: #333; } input { - font-size: 12pt; + font-size: 14pt; } h1 { @@ -107,10 +107,17 @@ font-family: sans-serif; font-size: 10px; } -#lists-sidebar-item, .have-lists #no-lists-sidebar-item { +#lists-sidebar-item, +.have-lists .have-no-lists { display: none; } .have-lists #lists-sidebar-item { display: block; } +div.needs-lists { display: none; } +.have-lists div.needs-lists { display: block; } +.have-list-items #list-is-empty { display: none; } +#list-is-empty { + font-style: italic; +} .table-2-col { display: grid; grid-template-columns: auto auto; @@ -119,15 +126,44 @@ font-size: 10px; .table-2-col > .arrow { align-self: center; } -#list-contents { display: none; } .have-list #list-contents { display: block; } -#new-list-sidebar-item > div { +.input-with-button { display: grid; grid-template-columns: auto auto; column-gap: 1ex; } +.input-with-button > button { + max-width: max-content; +} + +.list-item-row { + display: grid; + grid-template-columns: max-content auto max-content; + column-gap: 1ex; + background: hsla(0, 0%, 90%, 0); + transition: 0.5s; +} +.list-item-row:hover { + background: hsla(0, 0%, 90%, 1); +} +.list-item-row .description { cursor: text; min-width: 2em; } +.list-item-row .edit-trigger { + cursor: pointer; + visibility: hidden; + opacity: 0; + transition: 0.5s; + background: hsla(0, 0%, 80%, 0); +} +.list-item-row:hover .edit-trigger { + visibility: visible; + opacity: 1; + padding: 0 0.5ex; +} +.edit-trigger:hover { + background: hsla(0, 0%, 80%, 1); +} #busy-screen { display: none; @@ -156,4 +192,17 @@ font-size: 10px; background: hsla(0, 0%, 100%, 75%); border-radius: 1ex; } - +ul#lists > li { + cursor: pointer; + border: 1px solid transparent; + transition: 0.25s; +} +ul#lists > li:hover { + border: 1px solid black; +} +ul#lists > li.selected { + background: hsl(0, 0%, 70%); +} +#new-list-item { + margin-top: 1em; +} diff --git a/public/javascripts/lsl.js b/public/javascripts/lsl.js index 8ecf08e..a21cb3b 100644 --- a/public/javascripts/lsl.js +++ b/public/javascripts/lsl.js @@ -1,20 +1,153 @@ "use strict"; (function(){ var ui_icon_class_re = new RegExp('\\bui-icon-\\S+\\b'); -var uri_base; -var lists_version; +var uri_base; // filled on page load from an HTML attribute +var lists_version = -1; var lists = []; -var current_list; +var selected_list; +function add_list_item(data) { + var item = $('
  • ').addClass('list-item-row').data('item', data); + var cb = $(''); + if (data.done) cb.prop('checked', true); + item.append(cb); + item.append($('').text(data.description || '')); + item.append($('').text('…')); + + $('#list-items').append(item).addClass('have-list-items'); +} +function got_lists_version(new_version) { + if (new_version != lists_version) + window.setTimeout(load_lists); +} +function load_list_items(uri, target) { + $.get(uri) + .done(item_data => { + target.data('items', item_data); + if (selected_list) + selected_list.removeClass('selected'); + + var item_list = $('#list-items').empty().removeClass('have-list-items'); + + $.each(item_data.items, (i,item) => { + add_list_item(item); + }); + + got_lists_version(item_data.lists_version); + }); +} +function select_list(new_selected_list) { + if (new_selected_list == selected_list) + return; + + var id_data = new_selected_list.data('id'); + + load_list_items(id_data.uri, new_selected_list); + selected_list = new_selected_list; + selected_list.addClass('selected'); + $('#selected-list-name').text(id_data.name); + +} function load_lists() { - console.log('TODO'); + $.get(uri_base + '/api/v1/list') + .done(data => { + lists_version = data.lists_version; + + if (data.lists.length) { + var lists = $('#lists'); + var new_selected_list; + + $.each(data.lists, (i, list) => { + var my_id = 'shopping-list-id'+i; + var list_item = $('
  • ') + .data('id', list) + .prop('id', my_id) + .text(list.name); + + lists.append(list_item); + + if (!new_selected_list + ||selected_list && selected_list.uri == list.uri) + new_selected_list = list_item; + + }); + + select_list(new_selected_list); + + $('#page').addClass('have-lists'); + selected_list = new_selected_list; + } + else { + $('#page').removeClass('have-lists'); + selected_list = null; + } + }); +} +function new_list_submission_done(data) { + if (data.lists_version != lists_version) { + load_lists(); + return; + } + + console.log("TODO: easy-add new list"); + } function handle_new_list_submission(){ $.post(uri_base + '/api/v1/list', JSON.stringify({name:$('input[name="list_name"]').val()})) - .done(load_lists); + .done(new_list_submission_done); return false; } +function new_list_item_submission_done(data) { + if (data.lists_version != lists_version) { + load_lists(); + return; + } + + var item_data = selected_list.data('items'); + + if (data.list_version != item_data.version + 1) { + load_list_items(current_list); + } + else { + var new_item = { + description: $('#new-list-item input[type="text"]').val().trim(), + done: $('#new-list-item input[type="checkbox"]').prop('checked'), + version: 1, + }; + item_data.items.push(new_item); + item_data.version = data.list_version; + add_list_item(new_item); + } + + $('#new-list-item input').val(''); +} +function handle_new_list_item_submission(){ + var description = $('#new-list-item input[type="text"]').val().trim(); + if (description.length == 0) return; + + var id_data = selected_list.data('id'); + $.post(id_data.uri, + JSON.stringify({ + description: description, + done: $('#new-list-item input[type="checkbox"]').prop('checked') + }), + ) + .done(new_list_item_submission_done); +} +function handle_list_item_state_changed(ev) { + var item = $(ev.target).closest('li'); + var cb = item.find('input[type="checkbox"]'); + var item_data = item.data('item'); + + $.ajax(item_data.uri, + { type: 'PUT', + data: JSON.stringify({ + version: item_data.version, + done: cb.prop('checked')}) + } + ); +} $(function(){ uri_base = $('#page').attr('lsl-uri-base'); $(document).ajaxStart(function(){ @@ -49,6 +182,18 @@ $(function(){ return true; }); + $('ul#lists').on('click', 'li', ev=>{ + select_list($(ev.target)); + }); + $('#new-list-item button').on('click', handle_new_list_item_submission); + $('#new-list-item input').on('keypress', ev => { + if (13 == ev.keyCode) { + handle_new_list_item_submission(); + return false; + } + return true; + }); + $('#list-items').on('change', '.list-item-row input[type="checkbox"]', handle_list_item_state_changed); load_lists(); }); })(); diff --git a/views/index.tt b/views/index.tt index 6f32f0c..0b7a8ce 100644 --- a/views/index.tt +++ b/views/index.tt @@ -3,17 +3,17 @@