forked from cory/tildefriends
182 lines
4.2 KiB
JavaScript
182 lines
4.2 KiB
JavaScript
|
import {LitElement, html} from './lit-core.min.js';
|
||
|
import * as tfrpc from '/static/tfrpc.js';
|
||
|
|
||
|
class TodosElement extends LitElement {
|
||
|
static get properties() {
|
||
|
return {
|
||
|
lists: {type: Array}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
constructor() {
|
||
|
super();
|
||
|
this.lists = [];
|
||
|
let self = this;
|
||
|
tfrpc.rpc.todo_get_all().then(function(lists) {
|
||
|
self.lists = lists;
|
||
|
}).catch(function(error) {
|
||
|
console.log(error);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
async new_list() {
|
||
|
await tfrpc.rpc.todo_add('new list');
|
||
|
await this.refresh();
|
||
|
}
|
||
|
|
||
|
async refresh() {
|
||
|
this.lists = await tfrpc.rpc.todo_get_all();
|
||
|
}
|
||
|
|
||
|
render() {
|
||
|
return html`
|
||
|
<div>
|
||
|
<div style="display: flex">
|
||
|
${this.lists.map(x => html`
|
||
|
<tf-todo-list name=${x.name} .items=${x.items} @change=${this.refresh}></tf-todo-list>
|
||
|
`)}
|
||
|
</div>
|
||
|
<input type="button" @click=${this.new_list} value="+ List"></input>
|
||
|
</div>`;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class TodoListElement extends LitElement {
|
||
|
static get properties() {
|
||
|
return {
|
||
|
name: {type: String},
|
||
|
items: {type: Array},
|
||
|
editing: {type: Number},
|
||
|
editing_name: {type: Boolean},
|
||
|
};
|
||
|
}
|
||
|
|
||
|
constructor() {
|
||
|
super();
|
||
|
this.items = [];
|
||
|
}
|
||
|
|
||
|
save() {
|
||
|
tfrpc.rpc.todo_set(this.name, this.items).catch(function(error) {
|
||
|
console.log(error);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
remove_item(item) {
|
||
|
let index = this.items.indexOf(item);
|
||
|
this.items = [].concat(this.items.slice(0, index), this.items.slice(index + 1));
|
||
|
this.save();
|
||
|
}
|
||
|
|
||
|
handle_check(event, item) {
|
||
|
item.x = event.srcElement.checked;
|
||
|
this.save();
|
||
|
}
|
||
|
|
||
|
input_blur(item) {
|
||
|
this.save();
|
||
|
this.editing = undefined;
|
||
|
}
|
||
|
|
||
|
input_change(event, item) {
|
||
|
item.text = event.srcElement.value;
|
||
|
}
|
||
|
|
||
|
input_keydown(event, item) {
|
||
|
if (event.key === 'Enter' || event.key === 'Escape') {
|
||
|
item.text = event.srcElement.value;
|
||
|
this.editing = undefined;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
updated() {
|
||
|
let edit = this.renderRoot.getElementById('edit');
|
||
|
if (edit) {
|
||
|
edit.select();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
render_item(item) {
|
||
|
let index = this.items.indexOf(item);
|
||
|
let self = this;
|
||
|
if (index === this.editing) {
|
||
|
return html`
|
||
|
<div><input type="checkbox" ?checked=${item.x} @change=${x => self.handle_check(x, item)}></input>
|
||
|
<input
|
||
|
id="edit"
|
||
|
type="text"
|
||
|
value=${item.text}
|
||
|
@change=${event => self.input_change(event, item)}
|
||
|
@keydown=${event => self.input_keydown(event, item)}
|
||
|
@blur=${x => self.input_blur(item)}></input>
|
||
|
<span @click=${x => self.remove_item(item)}>x</span></div>
|
||
|
`;
|
||
|
} else {
|
||
|
return html`
|
||
|
<div><input type="checkbox" ?checked=${item.x} @change=${x => self.handle_check(x, item)}></input>
|
||
|
<span @click=${x => self.editing = index}>${item.text}</span>
|
||
|
<span @click=${x => self.remove_item(item)} style="cursor: pointer">❎</span></div>
|
||
|
`;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
add_item() {
|
||
|
this.items = [].concat(this.items || [], [{text: 'new item'}]);
|
||
|
this.editing = this.items.length - 1;
|
||
|
this.save();
|
||
|
}
|
||
|
|
||
|
async remove_list() {
|
||
|
if (confirm(`Are you sure you want to remove "${this.name}"?`)) {
|
||
|
await tfrpc.rpc.todo_remove(this.name);
|
||
|
this.dispatchEvent(new Event('change'));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
rename(new_name) {
|
||
|
let self = this;
|
||
|
return tfrpc.rpc.todo_rename(this.name, new_name).then(function() {
|
||
|
self.dispatchEvent(new Event('change'));
|
||
|
self.editing_name = false;
|
||
|
}).catch(function(error) {
|
||
|
console.log(error);
|
||
|
alert(error.message);
|
||
|
self.editing_name = false;
|
||
|
});
|
||
|
}
|
||
|
|
||
|
name_blur(new_name) {
|
||
|
this.rename(new_name);
|
||
|
}
|
||
|
|
||
|
name_keydown(event, item) {
|
||
|
let self = this;
|
||
|
if (event.key == 'Enter' || event.key === 'Escape') {
|
||
|
let new_name = event.srcElement.value;
|
||
|
this.rename(new_name);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
render() {
|
||
|
let self = this;
|
||
|
let name = this.editing_name ?
|
||
|
html`<input
|
||
|
type="text"
|
||
|
id="edit"
|
||
|
@keydown=${event => self.name_keydown(event)}
|
||
|
@blur=${event => self.name_blur(event.srcElement.value)}
|
||
|
value=${this.name}></input>` :
|
||
|
html`<h2 @click=${x => this.editing_name = true}>${this.name}</h2>`;
|
||
|
return html`
|
||
|
<div style="border: 3px solid black; padding: 8px; margin: 8px; border-radius: 8px; background-color: #444">
|
||
|
${name}
|
||
|
${(this.items || []).map(x => self.render_item(x))}
|
||
|
<button @click=${self.add_item}>+ Item</button>
|
||
|
<button @click=${self.remove_list}>- List</button>
|
||
|
</div>
|
||
|
`;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
customElements.define('tf-todo-list', TodoListElement);
|
||
|
customElements.define('tf-todos', TodosElement);
|