renderer.ts
This class is comprised of static functions to ease our interaction with the DOM
.
It has two primary entry points, first is todoList
. This function will take in a title text, a list of todo items and two callbacks, one for deleting an entry and one for making an entry complete/incomplete. This function will construct the appropriate DOM
nodes needed to display a list of todo items. The second entry point is showMessage
, this works a little differently, it will generate a DOM
node but it will also insert it into the page, it then waits 3 seconds and removes the node. This function takes the message text and a boolean for it it should have the error class added to it for styling purposes.
import ToDo from '../models/todo';
/**
* The main HTML constructor service, all methods are static
*/
export default class Renderer {
/**
* Create a new list of todo items to be inserted into the dom
* @param title The text to appear above the todo list
* @param list The items to render in the todo list
* @param removeCallback The function to call when the x button is clicked
* @param changeCallback The function to call when the checkbox is clicked
*/
static todoList(title: string, list: ToDo[], removeCallback: (id: number) => void, changeCallback: (id: number, newState: boolean) => void) {
let ret = document.createElement('div');
ret.setAttribute('class', `todo-list ${title.toLowerCase()}`);
let header = document.createElement('h2');
header.setAttribute('class', 'list-title');
header.appendChild(document.createTextNode(title));
ret.appendChild(header);
if (!list || list.length < 1) {
ret.appendChild(Renderer.dummy(`Nothing here!`));
} else {
list.forEach(todo => {
ret.appendChild(Renderer.todo(todo, removeCallback, changeCallback))
});
}
return ret;
}
/**
* Generate a single todo item to be inserted into the dom
* @param todo The todo item that will be rendered
* @param removeCallback The function to be called when the X button is clicked
* @param changeCallback The function to be called with the checkbox is clicked
*/
static todo(todo: ToDo, removeCallback: (id: number) => void, changeCallback: (id: number, newState: boolean) => void): HTMLDivElement {
let ret = document.createElement('div');
ret.setAttribute('class', 'todo-item');
let chk = Renderer.todoCheckbox(todo.id, todo.complete, changeCallback);
ret.appendChild(chk);
let sp = Renderer.todoAction(todo.action);
ret.appendChild(sp);
let btn = Renderer.todoRemove(todo.id)
btn.addEventListener('click', () => removeCallback(todo.id));
ret.appendChild(btn);
return ret;
}
/**
* Generate the checkbox for a todo item
* @param id The id of the todo item (this will be passed to the callbacks)
* @param complete The current state of the todo item (the inverse of this will be passed to the changeCallback)
* @param changeCallback The function to call when the checkbox is clicked
*/
static todoCheckbox(id: number, complete: boolean, changeCallback: (id: number, newState: boolean) => void): HTMLDivElement {
let chk = document.createElement('div');
chk.setAttribute('type', 'checkbox');
chk.setAttribute('class', 'todo-complete');
chk.setAttribute('id', `chk-${id}`)
chk.addEventListener('click', () => changeCallback(id, !complete));
let symbol = complete ? '✓' : '';
chk.appendChild(document.createTextNode(symbol));
return chk;
}
/**
* Generate the span element for a todo item
* @param action The text that should appear for the todo item
*/
static todoAction(action: string): HTMLSpanElement {
let sp = document.createElement('span');
sp.setAttribute('class', 'todo-action');
sp.appendChild(document.createTextNode(action));
return sp;
}
/**
* Generate the delete button for a todo item
* @param id the id of the todo item
*/
static todoRemove(id: number): HTMLButtonElement {
let btn = document.createElement('button');
btn.setAttribute('type', 'button');
btn.setAttribute('class', 'remove-button');
btn.setAttribute('id', `btn-${id}`);
btn.appendChild(document.createTextNode('✘'));
return btn;
}
static dummy(text: string): HTMLDivElement {
let ret = document.createElement('div');
ret.setAttribute('class', 'todo-item dummy');
ret.appendChild(Renderer.todoAction(text));
return ret;
}
/**
* Display an error message to the user
* @param text The message you want to display
* @param isError If the message should be styled as an error
*/
static showMessage(text: string, isError: boolean = false) {
let msg = document.createElement('div');
let cls = `message ${isError ? 'error' : ''}`.trim();
msg.setAttribute('class', cls);
msg.appendChild(document.createTextNode(text));
document.body.appendChild(msg);
setTimeout(() => document.body.removeChild(msg), 3000);
}
}