{
let wrapper = html`<div class="wrapper">
<!-- Title for the todo list -->
<div>
<h2>To Do List on ObservableHQ!</h2>
</div>
<!-- Input text box with placeholder text -->
<div>
<input type="text" class="text" size="10" placeholder="Type task here...">
</div>
<div>
<!-- Button to redo, which also adds the text box item to the list -->
<input type="button" class="redo" value="Add to List / Redo">
<!-- Button to undo any action -->
<input type="button" class="undo" value="Undo">
<!-- Button to clear all items on the list, both completed and not completed -->
<input type="button" class="clear" value="Clear All">
</div>
<!-- Showing the selection for whether to show all, only completed, or only active -->
<div class=visibilityFilterBoxDiv></div>
<!-- Showing the actual to do list itself with checked and unchecked items -->
<div class=todoList></div>
</div>`
function updateData() {
const item = d3.select('.todoList')
.selectAll('.item')
.data(store.getState().todos.present.filter(todo => {
switch (store.getState().visibilityFilter) {
case 'SHOW_ALL':
return todo
// When SHOW_COMPLETED is selected, then show the part of the todo list that has been marked as completed
case 'SHOW_COMPLETED':
return todo.completed === true
// When SHOW_ACTIVE is selected, then show the part of the todo list that has been marked as completed
case 'SHOW_ACTIVE':
return todo.completed === false
// set a default state as well
default:
console.warn('Unexpected visibilityFilter state')
return todo
}
}), d => d.index)
// update checkboxes for selections
const updateItemBox = item.select('.itemBox')
// entering the selections into the html
const itemEnter = item.enter()
.append('div')
// setting the specific class
.attr('class', d => `item ${d.text}`)
// setting the styles of the items
.style('text-align', 'center')
.style('font-family', 'monospace')
// enter selection checkboxes and merging with update checkboxes
const itemBox = itemEnter.append('input')
// setting the class and types here
.attr('class', 'itemBox')
.attr('type', 'checkbox')
.merge(updateItemBox)
.each(function(d){
// if it is marked as completed, then set the box as checked
if (d.completed) {
d3.select(this).attr('checked', true)
// if not, then do not check it
} else {
d3.select(this).attr('checked', null)
}
})
// Every time that there is a click on one of the item boxes, we must call toggleTodo and then update the view with the new information that it has been clicked
.on('click', d => {
store.dispatch(toggleTodo(d.index))
updateData()
})
// enter selection text
const itemText = itemEnter.append('label')
// setting the class and actual text
.attr('class', 'itemText')
.attr('for', d => d.text)
.text(d => d.text)
// exit selection
const itemExit = item.exit().remove()
}
// including the three visibility filter options here
const visibilityFilterBoxes = d3.select(wrapper)
.select('.visibilityFilterBoxDiv')
.selectAll('.visibilityFilters')
.data(Object.values(VisibilityFilters))
.join('div')
// adding the check boxes for the visibility options with the correct attributes
const visibilityCheckboxes = visibilityFilterBoxes.append('input')
.attr('class', 'visibilityCheckboxes')
.attr('type', 'radio')
.attr('name', 'visibilityCheckboxes')
.attr('id', d => d)
.attr('value', d => d)
// when clicked, it will set the visibility filter to the clicked one and update the view
.on('click', d => {
store.dispatch(setVisibilityFilter(d))
updateData()
})
// Set it as checked if the visiblity filter is currently on this setting
.each(function(d) {
if (store.getState().visibilityFilter === d) d3.select(this).attr('checked', true)
})
// choosing the atttributions, font choices, and actual text for each of the visibility settings
const visibilityLabels = visibilityFilterBoxes.append('label')
.attr('class', 'visibilityLabels')
.attr('for', d => d)
.text(d => d.charAt(0) + d.replace('_', ' ').toLowerCase().slice(1))
.style('font-family', 'Helvetica')
// adding a todo through the text box
d3.select(wrapper).select('.text')
.on('change', function() {
// getting the current length of the todo list as the new index where the next item should be entered
const index = store.getState().todos.present.length
// calling addTodo (defined below) to add the value in the index position
store.dispatch(addTodo(this.value, index))
// updating the view
updateData()
// resetting the text box value to blank
this.value = ''
})
// coding the response to clicking the undo button
const undoButton = d3.select(wrapper).select('.undo')
.on('click', () => {
// calling the ActionCreators.undo() function
store.dispatch(ActionCreators.undo())
// updating the view
updateData()
})
// coding the response to clicking the redo button
const redoButton = d3.select(wrapper).select('.redo')
.on('click', () => {
// calling the ActionCreators.redo() function
store.dispatch(ActionCreators.redo())
// updating the view
updateData()
})
// my new contribution: adding a clear all button
const clearButton = d3.select(wrapper).select('.clear')
.on('click', function() {
// calling the clearTodo function that I define separately
store.dispatch(clearTodo())
// updating the view
updateData()
})
return wrapper
}