Published
Edited
Sep 23, 2022
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
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() {
// updating the selection
const item = d3.select('.todoList')
.selectAll('.item')
.data(store.getState().todos.present.filter(todo => {
// getting the array of items on the todo list, setting as variable todo
switch (store.getState().visibilityFilter) {
// When SHOW_ALL is selected, then show the entire todo list
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
}
Insert cell
Insert cell
Insert cell
ADD_TODO = 'ADD_TODO'
Insert cell
TOGGLE_TODO = 'TOGGLE_TODO'
Insert cell
CLEAR_ALL = 'CLEAR_ALL'
Insert cell
SET_VISIBILITY_FILTER = 'SET_VISIBILITY_FILTER'
Insert cell
// The visibility filters are an object with all three possibilities defined together.
VisibilityFilters = {
return {
SHOW_ALL: 'SHOW_ALL',
SHOW_COMPLETED: 'SHOW_COMPLETED',
SHOW_ACTIVE: 'SHOW_ACTIVE',
}
}
Insert cell
Insert cell
// telling store.dispatch that the function to be executed is the ADD_TODO type, with the text and index position of the addition to follow
function addTodo(text, index) {
return { type: ADD_TODO, text, index }
}
Insert cell
// telling store.dispatch that the function to be executed is the CLEAR_ALL type. No need for text or index here since just getting blank array in return
function clearTodo() {
return { type: CLEAR_ALL }
}
Insert cell
// telling store.dispatch that the function to be executed is the TOGGLE_TODO type, with the index position of the toggled / checked item to follow
function toggleTodo(index) {
return { type: TOGGLE_TODO, index }
}
Insert cell
// telling store.dispatch that the function to be executed is the SET_VISIBILITY_FILTER type, with the specific filter to set it to to follow
function setVisibilityFilter(filter) {
return { type: SET_VISIBILITY_FILTER, filter }
}
Insert cell
Insert cell
// setting the intitial state prior to any data being entered
initialState = {
return {
// starting in the state with all being shown
visibilityFilter: VisibilityFilters.SHOW_ALL,
// starting with an empty todo list
todos: []
}
}
Insert cell
// setting the todos function and the specific things to do in each case
function todos(state = [], action) {
switch (action.type) {
// when ADD_TODO, setting the text and index to the input types and setting the check box to unchecked / uncompleted
case ADD_TODO:
return [
...state,
{
text: action.text,
completed: false,
index: action.index
}
]
// when TOGGLE_TODO, change the completion status of the checked box for the index that you ask for
case TOGGLE_TODO:
return state.map((todo, index) => {
if (index === action.index) {
return {
...todo,
completed: !todo.completed
}
}
return todo
})
// when CLEAR_ALL, just return the blank array as the cleared todo list
case CLEAR_ALL:
return []
// setting the default state
default:
return state
}
}
Insert cell
// enable the undoing of todos based on the imported package
undoableTodos = undoable(todos)
Insert cell
// setting the visibility filter if the function has been called
function visibilityFilter(state = VisibilityFilters.SHOW_ALL, action) {
switch (action.type) {
case SET_VISIBILITY_FILTER:
return action.filter
default:
return state
}
}
Insert cell
// setting the updated visibility filter and undoable todo list.
function todoApp(state = initialState, action) {
return {
visibilityFilter: visibilityFilter(state.visibilityFilter, action),
todos: undoableTodos(state.todos, action),
}
}
Insert cell
// storing the todoApp
store = redux.createStore(todoApp)
Insert cell
Insert cell
// including d3 library
d3 = require('d3@5')
Insert cell
// including redux library
redux = require('redux@4')

Insert cell
// including redux undo library
reduxUndo = (await require('https://cdn.jsdelivr.net/npm/redux-undo@1.0.0-beta9-9-7/dist/redux-undo.js'))
Insert cell
Insert cell
// setting default reduxUndo function to variable undoable
undoable = reduxUndo.default
Insert cell
// setting ActionCreators reduxUndo function to variable ActionCreators
ActionCreators = reduxUndo.ActionCreators

Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more