Skip to content

Commit

Permalink
NEW: Sort columns on click (#130)
Browse files Browse the repository at this point in the history
partially addresses #130
  • Loading branch information
dwhieb committed Jul 16, 2024
1 parent d68ac31 commit 2f7f10a
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 26 deletions.
2 changes: 1 addition & 1 deletion pages/Search/Search.css
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ h2 {
box-shadow: 0px 15px 0px -14px black;
}

.results {
#results {

border-collapse: separate; /* This is necessary for borders to stay fixed with position: sticky */
margin: 0;
Expand Down
20 changes: 10 additions & 10 deletions pages/Search/Search.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@

<span id=sort-hint>Click on a header column to sort.</span>

<table class=results>
<table id=results>
<caption class=visually-hidden id=results-caption><h2>Search Results</h2></caption>
<thead>
<tr class=meta-row>
Expand All @@ -66,44 +66,44 @@

{{!-- Component Fields --}}
{{#with sort }}
<th {{#if language }} aria-sort='{{ language }}' {{/if}} scope=col>
<button aria-describedby=sort-hint class=sort-button>
<th {{#if displayLanguage }} aria-sort='{{ displayLanguage }}' {{/if}} scope=col>
<button aria-describedby=sort-hint class=sort-button data-field=displayLanguage>
<span>Language</span>
<svg aria-hidden class=sort-icon><use href='{{#if (is language 'ascending') }}#ascending{{else if (is language 'descending') }}#descending{{else}}#sort{{/if}}'></svg>
<svg aria-hidden class=sort-icon><use href='{{#if (is displayLanguage 'ascending') }}#ascending{{else if (is displayLanguage 'descending') }}#descending{{else}}#sort{{/if}}'></svg>
</button>
</th>
<th {{#if form }} aria-sort='{{ form }}' {{/if}} scope=col>
<button aria-describedby=sort-hint class=sort-button>
<button aria-describedby=sort-hint class=sort-button data-field=form>
<span>Form</span>
<svg aria-hidden class=sort-icon><use href='{{#if (is form 'ascending') }}#ascending{{else if (is form 'descending') }}#descending{{else}}#sort{{/if}}'></svg>
</button>
</th>
<th {{#if UR }} aria-sort='{{ UR }}' {{/if}} scope=col>
<button aria-describedby=sort-hint class=sort-button>
<button aria-describedby=sort-hint class=sort-button data-field=UR>
<span>UR</span>
<svg aria-hidden class=sort-icon><use href='{{#if (is UR 'ascending') }}#ascending{{else if (is UR 'descending') }}#descending{{else}}#sort{{/if}}'></svg>
</button>
</th>
<th {{#if PA }} aria-sort='{{ PA }}' {{/if}} scope=col>
<button aria-describedby=sort-hint class=sort-button>
<button aria-describedby=sort-hint class=sort-button data-field=PA>
<span>Proto-Algonquian</span>
<svg aria-hidden class=sort-icon><use href='{{#if (is PA 'ascending') }}#ascending{{else if (is PA 'descending') }}#descending{{else}}#sort{{/if}}'></svg>
</button>
</th>
<th {{#if definition }} aria-sort='{{ definition }}' {{/if}} scope=col>
<button aria-describedby=sort-hint class=sort-button>
<button aria-describedby=sort-hint class=sort-button data-field=definition>
<span>Definition</span>
<svg aria-hidden class=sort-icon><use href='{{#if (is definition 'ascending') }}#ascending{{else if (is definition 'descending') }}#descending{{else}}#sort{{/if}}'></svg>
</button>
</th>
<th {{#if type }} aria-sort='{{ type }}' {{/if}} scope=col>
<button aria-describedby=sort-hint class=sort-button>
<button aria-describedby=sort-hint class=sort-button data-field=type>
<span>Type</span>
<svg aria-hidden class=sort-icon><use href='{{#if (is type 'ascending') }}#ascending{{else if (is type 'descending') }}#descending{{else}}#sort{{/if}}'></svg>
</button>
</th>
<th {{#if subcategory }} aria-sort='{{ subcategory }}' {{/if}} scope=col>
<button aria-describedby=sort-hint class=sort-button>
<button aria-describedby=sort-hint class=sort-button data-field=subcategory>
<span>Subcategory</span>
<svg aria-hidden class=sort-icon><use href='{{#if (is subcategory 'ascending') }}#ascending{{else if (is subcategory 'descending') }}#descending{{else}}#sort{{/if}}'></svg>
</button>
Expand Down
23 changes: 8 additions & 15 deletions pages/Search/Search.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import SortDirectives from '../../scripts/SortDirectives.js'

/**
*
* @param {URL} url A URL object.
Expand Down Expand Up @@ -44,28 +46,22 @@ export function Search(req, res) {

// Sort

sort = sort
.split(`,`)
.filter(Boolean)
.map(directive => ({
ascending: !directive.startsWith(`-`),
field: directive.replace(/^-/v, ``),
}))
sort = new SortDirectives(sort)

if (sort.length) {
if (sort.size) {
allResults.sort((a, b) => {

const comparisons = sort.map(({ ascending, field }) => {
const comparisons = Array.from(sort.entries())
.map(([field, direction]) => {
const comparison = (a[field] || ``).localeCompare(b[field] || ``)
return ascending ? comparison : comparison * -1
return direction === `ascending` ? comparison : comparison * -1
})

return comparisons.reduce((state, comparison) => state ? state : comparison, 0)

})
}


// Pagination

limit = Number(limit)
Expand Down Expand Up @@ -141,10 +137,7 @@ export function Search(req, res) {
startIndex: (offset + 1).toLocaleString(),
},
results,
sort: sort.reduce((hash, { ascending, field }) => {
hash[field] = ascending ? `ascending` : `descending`
return hash
}, {}),
sort: Object.fromEntries(sort),
totalResults: allResults.length.toLocaleString(),
})

Expand Down
7 changes: 7 additions & 0 deletions pages/Search/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import Copier from '../../scripts/Copier.js'
import SearchBox from './scripts/SearchBox.js'
import Table from './scripts/Table.js'

// Initialize button to copy citation information

Expand All @@ -18,3 +19,9 @@ if (button && el) {
const search = new SearchBox

search.initialize()

// Initialize results table

const table = new Table

table.initialize()
36 changes: 36 additions & 0 deletions pages/Search/scripts/Table.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/* global document, window */

import SortDirectives from '../../../scripts/SortDirectives.js'

const directivesOrder = [null, `ascending`, `descending`, null]

export default class Table {

initialize() {
this.el = document.getElementById(`results`)
if (!this.el) return
this.el.addEventListener(`click`, this.sort.bind(this))
}

sort(ev) {

const button = ev.target.closest(`button`)

if (!button) return

const { field } = button.dataset
const th = button.parentNode
const direction = th.ariaSort
const url = new URL(window.location.href)
let sort = url.searchParams.get(`sort`)
const directives = new SortDirectives(sort)
const directive = directivesOrder[directivesOrder.indexOf(direction) + 1]

directives.add(field, directive)
sort = directives.serialize()
url.searchParams.set(`sort`, sort)
window.location = url.href

}

}
51 changes: 51 additions & 0 deletions scripts/SortDirectives.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
export default class SortDirectives extends Map {

constructor(directives = ``) {

super()

if (!directives) return

directives.split(`,`)
.filter(Boolean)
.forEach(directive => {

const field = directive.replace(/^-/v, ``)
const direction = directive.startsWith(`-`) ? `descending` : `ascending`

this.set(field, direction)

})

}

/**
* Add a new sort directive to the list of directives. NOTE: The directive will be set as first in the insertion order.
* @param {String} field The field to set a new sort direction for.
* @param {"ascending"|"descending"} direction The sort direction to use for the field.
*/
add(field, direction) {

this.delete(field)

const entries = Array.from(this.entries())

if (direction) {
entries.unshift([field, direction])
}

this.clear()

for (const [f, d] of entries) {
this.set(f, d)
}

}

serialize() {
return Array.from(this.entries())
.map(([field, direction]) => `${ direction === `descending` ? `-` : `` }${ field }`)
.join(`,`)
}

}

0 comments on commit 2f7f10a

Please sign in to comment.