mirror of
https://github.com/starshardstudio/peafowl.git
synced 2024-11-24 22:14:19 +00:00
Add sorting to the games list
This commit is contained in:
parent
efe89dda2b
commit
0cc044ddd7
10 changed files with 296 additions and 18 deletions
3
.idea/dictionaries/steffo.xml
Normal file
3
.idea/dictionaries/steffo.xml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<component name="ProjectDictionaryState">
|
||||||
|
<dictionary name="steffo" />
|
||||||
|
</component>
|
|
@ -3,10 +3,18 @@
|
||||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
<exclude-output />
|
<exclude-output />
|
||||||
<content url="file://$MODULE_DIR$">
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/_static" type="java-resource" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/games" type="java-resource" />
|
<sourceFolder url="file://$MODULE_DIR$/games" type="java-resource" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/_site" />
|
<excludeFolder url="file://$MODULE_DIR$/_site" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="inheritedJdk" />
|
<orderEntry type="inheritedJdk" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
</component>
|
</component>
|
||||||
|
<component name="TemplatesService">
|
||||||
|
<option name="TEMPLATE_FOLDERS">
|
||||||
|
<list>
|
||||||
|
<option value="$MODULE_DIR$/_includes" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
</module>
|
</module>
|
|
@ -1,6 +1,6 @@
|
||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="Run" type="DenoConfigurationType" inputPath="$PROJECT_DIR$/_run.ts" programParameters="run --allow-read --allow-write --allow-net --allow-env --allow-run">
|
<configuration default="false" name="Run" type="DenoConfigurationType" inputPath="$PROJECT_DIR$/_run.ts" programParameters="run --allow-read --allow-write --allow-net --allow-env --allow-run">
|
||||||
<option name="applicationArguments" value="--watch" />
|
<option name="applicationArguments" value="--watch --host=0.0.0.0" />
|
||||||
<method v="2" />
|
<method v="2" />
|
||||||
</configuration>
|
</configuration>
|
||||||
</component>
|
</component>
|
|
@ -4,10 +4,10 @@ import {Progress, progressToClassName, progressToIconDef, progressToTitle} from
|
||||||
import {ratingToClassName} from "../_utils/rating.ts"
|
import {ratingToClassName} from "../_utils/rating.ts"
|
||||||
|
|
||||||
|
|
||||||
export type GameRowColumnKind = "rating" | "progress" | "name" | "hascontent" | "date" | "hoursplayed"
|
export type GameRowColumnKind = "rating" | "progress" | "name" | "namesort" | "hascontent" | "date" | "hoursplayed"
|
||||||
export type GameRowColumnPriority = undefined | "rating" | "progress" | "mixed"
|
export type GameRowColumnPriority = undefined | "rating" | "progress" | "mixed"
|
||||||
|
|
||||||
export const gameRowColumnKindDefault: GameRowColumnKind[] = ["rating", "name", "hascontent", "date", "progress", "hoursplayed"]
|
export const gameRowColumnKindDefault: GameRowColumnKind[] = ["rating", "name", "namesort", "hascontent", "date", "progress", "hoursplayed"]
|
||||||
|
|
||||||
|
|
||||||
export type GameRowProps = {
|
export type GameRowProps = {
|
||||||
|
@ -62,6 +62,13 @@ export function GameRow({game, columns = gameRowColumnKindDefault, priority}: Ga
|
||||||
</td>
|
</td>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
case "namesort": {
|
||||||
|
return (
|
||||||
|
<td key={index} className={`review-namesort`} hidden={true}>
|
||||||
|
<data value={game.name ?? ""}/>
|
||||||
|
</td>
|
||||||
|
)
|
||||||
|
}
|
||||||
case "hascontent": {
|
case "hascontent": {
|
||||||
return (
|
return (
|
||||||
<td key={index} className={`review-hascontent`}>
|
<td key={index} className={`review-hascontent`}>
|
||||||
|
|
|
@ -3,13 +3,14 @@ import {GameRow, GameRowColumnKind, gameRowColumnKindDefault, GameRowColumnPrior
|
||||||
|
|
||||||
|
|
||||||
export type GameTableProps = {
|
export type GameTableProps = {
|
||||||
|
id?: string,
|
||||||
games: GameData[],
|
games: GameData[],
|
||||||
columns?: GameRowColumnKind[]
|
columns?: GameRowColumnKind[]
|
||||||
priority?: GameRowColumnPriority
|
priority?: GameRowColumnPriority
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function GameTable({games, columns = gameRowColumnKindDefault, priority}: GameTableProps) {
|
export function GameTable({id, games, columns = gameRowColumnKindDefault, priority}: GameTableProps) {
|
||||||
const colElements = columns.map((column, index) => {
|
const colElements = columns.map((column, index) => {
|
||||||
switch(column) {
|
switch(column) {
|
||||||
case "rating": return (
|
case "rating": return (
|
||||||
|
@ -56,7 +57,15 @@ export function GameTable({games, columns = gameRowColumnKindDefault, priority}:
|
||||||
</abbr>
|
</abbr>
|
||||||
</th>
|
</th>
|
||||||
)
|
)
|
||||||
case "hascontent": return (
|
case "namesort": return (
|
||||||
|
<th key={index} scope={"col"} className={`review-namesort`} hidden={true}>
|
||||||
|
<abbr title={"The title to sort the game as."}>
|
||||||
|
Sort by
|
||||||
|
</abbr>
|
||||||
|
</th>
|
||||||
|
)
|
||||||
|
case "hascontent":
|
||||||
|
return (
|
||||||
<th key={index} scope={"col"} className={`review-hascontent`}>
|
<th key={index} scope={"col"} className={`review-hascontent`}>
|
||||||
<abbr title={"Whether the review has textual content, or just metadata."}>
|
<abbr title={"Whether the review has textual content, or just metadata."}>
|
||||||
<i className={`fa-sharp fa-regular fa-bars-sort`}/>
|
<i className={`fa-sharp fa-regular fa-bars-sort`}/>
|
||||||
|
@ -86,7 +95,7 @@ export function GameTable({games, columns = gameRowColumnKindDefault, priority}:
|
||||||
))
|
))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<table>
|
<table id={id}>
|
||||||
<colgroup>
|
<colgroup>
|
||||||
{colElements}
|
{colElements}
|
||||||
</colgroup>
|
</colgroup>
|
||||||
|
|
|
@ -91,7 +91,9 @@ export default function(data: GlobalData, helpers: Lume.Helpers) {
|
||||||
</section>
|
</section>
|
||||||
)
|
)
|
||||||
|
|
||||||
const games_cols = (games.length > 0) ? (
|
const games_cols = (
|
||||||
|
games.length > 0
|
||||||
|
) ? (
|
||||||
<section className={"flex flex-v"}>
|
<section className={"flex flex-v"}>
|
||||||
<h2>
|
<h2>
|
||||||
Videogames
|
Videogames
|
||||||
|
@ -100,11 +102,6 @@ export default function(data: GlobalData, helpers: Lume.Helpers) {
|
||||||
<i className={"fa-sharp fa-solid fa-magnifying-glass"}/> View all
|
<i className={"fa-sharp fa-solid fa-magnifying-glass"}/> View all
|
||||||
</a>
|
</a>
|
||||||
</small>
|
</small>
|
||||||
<small>
|
|
||||||
<a href={helpers.url("/games/feed.rss")}>
|
|
||||||
<i className={"fa-sharp fa-solid fa-rss"}/> Feed
|
|
||||||
</a>
|
|
||||||
</small>
|
|
||||||
</h2>
|
</h2>
|
||||||
{active_games.length > 0 && (
|
{active_games.length > 0 && (
|
||||||
<div className={"flex flex-1"}>
|
<div className={"flex flex-1"}>
|
||||||
|
|
|
@ -13,15 +13,24 @@ export default function(data: Lume.Data, helpers: Lume.Helpers) {
|
||||||
) : null
|
) : null
|
||||||
|
|
||||||
const games: GameData[] = data.search.pages("game")
|
const games: GameData[] = data.search.pages("game")
|
||||||
.sort((a, b) => (b.rating - a.rating)) as GameData[]
|
|
||||||
|
|
||||||
const games_section = (
|
const games_section = (
|
||||||
<section id={"list-games-section-games"}>
|
<section id={"list-games-section-games"}>
|
||||||
<h2>
|
<h2>
|
||||||
Videogames list
|
Videogames list
|
||||||
|
<small>
|
||||||
|
<a href={helpers.url("/games/feed.rss")}>
|
||||||
|
<i className={"fa-sharp fa-solid fa-rss"}/> Feed
|
||||||
|
</a>
|
||||||
|
</small>
|
||||||
|
<small>
|
||||||
|
<a href={helpers.url("/games/index.json")}>
|
||||||
|
<i className={"fa-sharp fa-solid fa-brackets-curly"}/> JSON
|
||||||
|
</a>
|
||||||
|
</small>
|
||||||
</h2>
|
</h2>
|
||||||
<div>
|
<div>
|
||||||
<GameTable games={games} priority={"mixed"}/>
|
<GameTable id={"list-games-table"} games={games} priority={"mixed"}/>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
)
|
)
|
||||||
|
@ -30,6 +39,8 @@ export default function(data: Lume.Data, helpers: Lume.Helpers) {
|
||||||
<main id={"list-games-main"}>
|
<main id={"list-games-main"}>
|
||||||
{intro_section}
|
{intro_section}
|
||||||
{games_section}
|
{games_section}
|
||||||
|
<script src={"/_static/scripting/sort.js"}/>
|
||||||
|
<script src={"/_static/scripting/installSortOnLoad.js"}/>
|
||||||
</main>
|
</main>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
5
_static/scripting/installSortOnLoad.js
Normal file
5
_static/scripting/installSortOnLoad.js
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
window.onload = function() {
|
||||||
|
const tableId = document.querySelector("table").id
|
||||||
|
installSort(tableId)
|
||||||
|
sortTable(tableId, readRating, (a, b) => b - a)
|
||||||
|
}
|
237
_static/scripting/sort.js
Normal file
237
_static/scripting/sort.js
Normal file
|
@ -0,0 +1,237 @@
|
||||||
|
/**
|
||||||
|
* @param tableId {string}
|
||||||
|
* @param readFn {(a: HTMLTableRowElement) => string}
|
||||||
|
* @param compareFn {(a: string, b: string) => number}
|
||||||
|
*/
|
||||||
|
function sortTable(tableId, readFn, compareFn) {
|
||||||
|
console.debug("Sorting table `", tableId , "` with ", readFn, " and ", compareFn)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {HTMLTableElement | undefined}
|
||||||
|
*/
|
||||||
|
const table = document.getElementById(tableId)
|
||||||
|
|
||||||
|
if(!table) {
|
||||||
|
console.error("Table `", tableId, "` not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {HTMLTableSectionElement | undefined}
|
||||||
|
*/
|
||||||
|
const tbody = table.tBodies[0]
|
||||||
|
|
||||||
|
if(!tbody) {
|
||||||
|
console.error("Table body of`", tableId, "` not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {HTMLTableRowElement[]}
|
||||||
|
*/
|
||||||
|
const allRows = [...tbody.rows]
|
||||||
|
const undefinedRows = []
|
||||||
|
|
||||||
|
for(const row of allRows) {
|
||||||
|
if(readFn(row) === undefined) {
|
||||||
|
undefinedRows.push(row)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const definedRows = allRows.filter(row => readFn(row) !== undefined)
|
||||||
|
const sortedRows = definedRows.toSorted((a, b) => compareFn(readFn(a), readFn(b)))
|
||||||
|
|
||||||
|
let reverse = true
|
||||||
|
for(let idx = 0; idx < sortedRows.length; idx++) {
|
||||||
|
if(definedRows[idx] !== sortedRows[idx]) {
|
||||||
|
reverse = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(reverse) {
|
||||||
|
sortedRows.reverse()
|
||||||
|
}
|
||||||
|
|
||||||
|
const resultRows = sortedRows.concat(undefinedRows)
|
||||||
|
|
||||||
|
while(tbody.rows.length > 0) {
|
||||||
|
tbody.deleteRow(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const row of resultRows) {
|
||||||
|
tbody.appendChild(row)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param a {HTMLTableRowElement}
|
||||||
|
*/
|
||||||
|
function readNameSort(a) {
|
||||||
|
for(const cell of a.cells) {
|
||||||
|
if(cell.classList.contains("review-namesort")) {
|
||||||
|
const value = cell.firstElementChild.value
|
||||||
|
if(value === "") return undefined
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param a {HTMLTableRowElement}
|
||||||
|
*/
|
||||||
|
function readRating(a) {
|
||||||
|
for(const cell of a.cells) {
|
||||||
|
if(cell.classList.contains("review-rating")) {
|
||||||
|
const value = cell.firstElementChild.value
|
||||||
|
if(value === "") return undefined
|
||||||
|
return Number.parseInt(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param a {HTMLTableRowElement}
|
||||||
|
*/
|
||||||
|
function readHoursPlayed(a) {
|
||||||
|
for(const cell of a.cells) {
|
||||||
|
if(cell.classList.contains("game-hoursplayed")) {
|
||||||
|
const value = cell.firstElementChild.value
|
||||||
|
if(value === "0") return undefined
|
||||||
|
return Number.parseInt(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param a {HTMLTableRowElement}
|
||||||
|
*/
|
||||||
|
function readDate(a) {
|
||||||
|
for(const cell of a.cells) {
|
||||||
|
if(cell.classList.contains("review-date")) {
|
||||||
|
const value = cell.firstElementChild.dateTime
|
||||||
|
if(value === "") return undefined
|
||||||
|
return new Date(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param a {HTMLTableRowElement}
|
||||||
|
*/
|
||||||
|
function readHasContent(a) {
|
||||||
|
for(const cell of a.cells) {
|
||||||
|
if(cell.classList.contains("review-hascontent")) {
|
||||||
|
const value = cell.firstElementChild.value
|
||||||
|
return value === "true"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param a {HTMLTableRowElement}
|
||||||
|
*/
|
||||||
|
function readProgress(a) {
|
||||||
|
for(const cell of a.cells) {
|
||||||
|
if(cell.classList.contains("game-progress")) {
|
||||||
|
/**
|
||||||
|
* @type {HTMLDataElement}
|
||||||
|
*/
|
||||||
|
const data = cell.firstElementChild
|
||||||
|
switch (data.value) {
|
||||||
|
case undefined:
|
||||||
|
return undefined;
|
||||||
|
case "unset":
|
||||||
|
return undefined;
|
||||||
|
case "notapplicable":
|
||||||
|
return 5;
|
||||||
|
case "new":
|
||||||
|
return 10;
|
||||||
|
case "started":
|
||||||
|
return 20;
|
||||||
|
case "beaten":
|
||||||
|
return 30;
|
||||||
|
case "completed":
|
||||||
|
return 40;
|
||||||
|
case "mastered":
|
||||||
|
return 50;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param tableId {string}
|
||||||
|
*/
|
||||||
|
function installSort(tableId) {
|
||||||
|
console.debug("Installing sorting capabilities on `", tableId, "`...")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {HTMLTableElement | undefined}
|
||||||
|
*/
|
||||||
|
const table = document.getElementById(tableId)
|
||||||
|
|
||||||
|
if(!table) {
|
||||||
|
console.error("Table `", tableId, "` not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {HTMLTableSectionElement | undefined}
|
||||||
|
*/
|
||||||
|
const thead = table.tHead
|
||||||
|
|
||||||
|
if(!thead) {
|
||||||
|
console.error("Table header of `", tableId, "` not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {HTMLTableRowElement | undefined}
|
||||||
|
*/
|
||||||
|
const thRow = thead.rows[0]
|
||||||
|
|
||||||
|
if(!thRow) {
|
||||||
|
console.error("Table header of `", tableId, "` not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const cell of thRow.cells) {
|
||||||
|
if(cell.classList.contains("review-name")) {
|
||||||
|
cell.onclick = function() {
|
||||||
|
sortTable(tableId, readNameSort, (a, b) => a.localeCompare(b))
|
||||||
|
}
|
||||||
|
cell.classList.add("sortable")
|
||||||
|
}
|
||||||
|
else if(cell.classList.contains("review-rating")) {
|
||||||
|
cell.onclick = function() {
|
||||||
|
sortTable(tableId, readRating, (a, b) => b - a)
|
||||||
|
}
|
||||||
|
cell.classList.add("sortable")
|
||||||
|
}
|
||||||
|
else if(cell.classList.contains("review-date")) {
|
||||||
|
cell.onclick = function() {
|
||||||
|
sortTable(tableId, readDate, (a, b) => b - a)
|
||||||
|
}
|
||||||
|
cell.classList.add("sortable")
|
||||||
|
}
|
||||||
|
else if(cell.classList.contains("game-hoursplayed")) {
|
||||||
|
cell.onclick = function() {
|
||||||
|
sortTable(tableId, readHoursPlayed, (a, b) => b - a)
|
||||||
|
}
|
||||||
|
cell.classList.add("sortable")
|
||||||
|
}
|
||||||
|
else if(cell.classList.contains("review-hascontent")) {
|
||||||
|
cell.onclick = function() {
|
||||||
|
sortTable(tableId, readHasContent, (a, b) => b - a)
|
||||||
|
}
|
||||||
|
cell.classList.add("sortable")
|
||||||
|
}
|
||||||
|
else if(cell.classList.contains("game-progress")) {
|
||||||
|
cell.onclick = function() {
|
||||||
|
sortTable(tableId, readProgress, (a, b) => b - a)
|
||||||
|
}
|
||||||
|
cell.classList.add("sortable")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -74,6 +74,7 @@ export function progressToTitle(progress?: Progress): string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Duplicated in _static/scripting/sort.js
|
||||||
export function progress_to_number(progress?: Progress): number {
|
export function progress_to_number(progress?: Progress): number {
|
||||||
switch (progress) {
|
switch (progress) {
|
||||||
case undefined:
|
case undefined:
|
||||||
|
|
Loading…
Reference in a new issue