Skip to content

Commit

Permalink
Add keyboard navigation to books pages, fiduswriter/fiduwriter#1279
Browse files Browse the repository at this point in the history
  • Loading branch information
johanneswilm committed Feb 7, 2025
1 parent 6bfc063 commit ac2e367
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 10 deletions.
2 changes: 0 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,6 @@ jobs:
pip install packaging
pip install webdriver-manager
pip install selenium
pip install flake8
pip install black
coverage run $(which fiduswriter) setup --no-static
- name: Run tests
uses: nick-invision/retry@v3
Expand Down
105 changes: 99 additions & 6 deletions fiduswriter/book/static/js/modules/books/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import deepEqual from "fast-deep-equal"
import {DataTable} from "simple-datatables"
import {keyName} from "w3c-keyname"

import * as plugins from "../../plugins/books_overview"
import {
Expand Down Expand Up @@ -172,7 +173,7 @@ export class BookOverview {
fileList.unshift([
"-1",
"top",
"",
false,
`<a class="fw-data-table-title fw-link-text parentdir" href="/books${encodeURI(
parentPath
)}" data-path="${parentPath}">
Expand Down Expand Up @@ -218,14 +219,79 @@ export class BookOverview {
hidden: true
},
{
select: [2, 7, 8],
select: 2,
sortable: false,
type: "boolean"
},
{
select: [7, 8],
sortable: false
},
{
select: [this.lastSort.column],
sort: this.lastSort.dir
}
]
],
rowNavigation: true,
rowSelectionKeys: ["Enter", "Delete", " "],
tabIndex: 1,
rowRender: (row, tr, _index) => {
if (row.cells[1].data === "folder") {
tr.childNodes[0].childNodes = []
return
}
const id = row.cells[0].data
const inputNode = {
nodeName: "input",
attributes: {
type: "checkbox",
class: "entry-select fw-check",
"data-id": id,
id: `book-${id}`
}
}
if (row.cells[2].data) {
inputNode.attributes.checked = true
}
tr.childNodes[0].childNodes = [
inputNode,
{
nodeName: "label",
attributes: {
for: `book-${id}`
}
}
]
}
})

this.table.on("datatable.selectrow", (rowIndex, event, focused) => {
event.preventDefault()
if (event.type === "keydown") {
const key = keyName(event)
if (key === "Enter") {
const link = this.table.dom.querySelector(
`tr[data-index="${rowIndex}"] a.fw-data-table-title`
)
if (link) {
link.click()
}
} else if (key === " ") {
const cell = this.table.data.data[rowIndex].cells[2]
cell.data = !cell.data
cell.text = String(cell.data)
this.table.update()
} else if (key === "Delete") {
const cell = this.table.data.data[rowIndex].cells[0]
const bookId = cell.data
this.mod.actions.deleteBookDialog([bookId], this.app)
}
} else {
if (!focused) {
this.table.dom.focus()
}
this.table.rows.setCursor(rowIndex)
}
})

this.table.on("datatable.sort", (column, dir) => {
Expand Down Expand Up @@ -276,7 +342,7 @@ export class BookOverview {
const row = [
"0",
"folder",
"",
false,
`<a class="fw-data-table-title fw-link-text subdir" href="/books${encodeURI(
this.path + subdir
)}/" data-path="${this.path}${subdir}/">
Expand All @@ -300,9 +366,9 @@ export class BookOverview {

// This is the folder of the file. Return the file.
return [
String(book.id),
book.id,
"file",
`<input type="checkbox" class="entry-select fw-check" data-id="${book.id}" id="book-${book.id}"><label for="book-${book.id}"></label>`,
false, // checkbox
`<span class="fw-data-table-title fw-inline fw-link-text" data-id="${
book.id
}">
Expand Down Expand Up @@ -395,6 +461,23 @@ export class BookOverview {
this.dom.addEventListener("click", event => {
const el = {}
switch (true) {
case findTarget(
event,
".entry-select, .entry-select + label",
el
): {
const checkbox = el.target
const dataIndex = checkbox
.closest("tr")
.getAttribute("data-index", null)
if (dataIndex) {
const index = Number.parseInt(dataIndex)
const data = this.table.data.data[index]
data.cells[2].data = !checkbox.checked
data.cells[2].text = String(!checkbox.checked)
}
break
}
case findTarget(event, ".delete-book", el): {
if (this.app.isOffline()) {
addAlert(
Expand Down Expand Up @@ -517,4 +600,14 @@ export class BookOverview {
this.dom.querySelectorAll(".entry-select:checked:not(:disabled)")
).map(el => Number.parseInt(el.getAttribute("data-id")))
}

close() {
if (this.table) {
this.table.destroy()
}
if (this.menu) {
this.menu.destroy()
this.menu = null
}
}
}
3 changes: 3 additions & 0 deletions fiduswriter/book/static/js/modules/books/menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const menuModel = () => ({
{
type: "text",
title: gettext("Create new book"),
keys: "n",
action: overview => {
overview.getImageDB().then(() => {
overview.mod.actions.createBookDialog(0, overview.imageDB)
Expand All @@ -25,6 +26,7 @@ export const menuModel = () => ({
{
type: "text",
title: gettext("Create new folder"),
keys: "o",
action: overview => {
const dialog = new NewFolderDialog(folderName => {
overview.path = overview.path + folderName + "/"
Expand All @@ -39,6 +41,7 @@ export const menuModel = () => ({
type: "search",
icon: "search",
title: gettext("Search books"),
keys: "s",
input: (overview, text) => {
if (text.length && !currentlySearching) {
overview.initTable(true)
Expand Down
3 changes: 2 additions & 1 deletion fiduswriter/book/static/js/plugins/menu/books.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ export class BookMenuItem {
title: gettext("compose books"),
url: "/books/",
text: gettext("Books"),
order: 5
order: 5,
keys: "Alt-o"
})
}
}
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ authors = [
classifiers=[
"Environment :: Web Environment",
"Framework :: Django",
"Framework :: Django :: 4.1",
"Framework :: Django :: 5.1",
"Intended Audience :: Developers",
"License :: OSI Approved :: GNU Affero General Public License v3",
"Operating System :: OS Independent",
Expand Down

0 comments on commit ac2e367

Please sign in to comment.