Docs: Upgrade reference to support categorical display

It even persists your preference using localStorage!
This commit is contained in:
Starbeamrainbowlabs 2021-10-05 02:02:53 +01:00
parent 5f2d5216af
commit c8773efe22
Signed by: sbrl
GPG Key ID: 1BE5172E637709C2
6 changed files with 348 additions and 31 deletions

View File

@ -2,23 +2,30 @@ const fs = require("fs");
const path = require("path");
const parse_sections = require("./lib/parse_sections.js");
const sections = parse_sections(fs.readFileSync(
path.resolve(
__dirname,
`../Chat-Command-Reference.md`
),
"utf-8"
));
let { sections, categories } = parse_sections(fs.readFileSync(
path.resolve(
__dirname,
`../Chat-Command-Reference.md`
),
"utf-8"
))
console.log(`REFERENCE SECTION TITLES`, sections.slice(1)
.sort((a, b) => a.title.localeCompare(b.title)).map(s => s.title));
sections = sections.slice(1).sort((a, b) => a.title.replace(/^\/+/g, "").localeCompare(
b.title.replace(/^\/+/g, "")));
console.log(`REFERENCE SECTION TITLES`)
console.log(sections
.map(s => [s.category, s.title].join(`\t`)).join(`\n`));
console.log(`************************`);
console.log(`REFERENCE SECTION COLOURS`, categories);
module.exports = {
layout: "theme.njk",
title: "Reference",
tags: "navigable",
date: "2001-01-01",
section_intro: sections[0],
sections_help: sections.slice(1)
.sort((a, b) => a.title.localeCompare(b.title))
sections_help: sections, // Remove the very beginning bit
categories: [...categories.keys()].join("|")
}

View File

@ -26,16 +26,23 @@
<section class="panel-generic">
<h2 id="contents" class="linked-section-heading">
<a class="section-link" href="#{{ section.slug }}">&#x1f517; <!-- Link Symbol --></a>
<span>Contents</span>
<span class="title">Contents</span>
</h2>
<p>TODO: Group commands here by category (*especially* the meta commands)</p>
<ul class="command-list">
{% for section in sections_help %}
<li data-filtermode-force="all"><a href="#{{ section.slug }}">
<code>{{ section.title }}</code>
</a></li>
{% endfor %}
</ul>
<div class="command-ordering-tabs">
<button class="active" data-mode="alphabetical">Alphabetical</button>
<button data-mode="categorical">Categorical</button>
</div>
<input type="hidden" id="category-names" value="{{ categories }}" />
<div class="command-container">
<h3>Alphabetical</h3>
<ul class="command-list">
{% for section in sections_help %}
<li data-filtermode-force="all" data-category="{{ section.category }}" style="--cat-colour: {{ section.category_colour }};"><a href="#{{ section.slug }}">
<code>{{ section.title }}</code>
</a></li>
{% endfor %}
</ul>
</div>
</section>
<script>
@ -43,6 +50,81 @@
return text.toLocaleLowerCase().includes(query);
}
function handle_display_mode(event) {
set_display_mode(event.target.dataset.mode);
localStorage.setItem("commandlist-displaymode", event.target.dataset.mode);
}
function sort_command_list(list) {
list.sort((a, b) =>
a.querySelector("a").href.localeCompare(b.querySelector("a").href))
}
function nodelist2array(list) {
const result = [];
for(let i = 0; i < list.length; i++) result.push(list[i]);
return result;
}
function set_display_mode(mode) {
console.info(`SET DISPLAYMODE ${mode}`)
const commands = nodelist2array(document.querySelectorAll(`.command-list li`));
const container = document.querySelector(`.command-container`);
switch(mode) {
case "categorical":
const sections = Object.create(null);
for(const command of [...commands]) {
const cat = command.dataset.category;
if(!(sections[cat] instanceof Array))
sections[cat] = [];
sections[cat].push(command);
}
const section_names = document.querySelector("#category-names").value
.split("|");
const fragment = new DocumentFragment();
for(const section_name of section_names) {
sort_command_list(sections[section_name]);
const header = document.createElement("h3");
header.appendChild(document.createTextNode(section_name));
fragment.appendChild(header);
const list = document.createElement("ul");
list.classList.add("command-list", "coloured");
for(const item of sections[section_name])
list.appendChild(item);
fragment.appendChild(list);
}
container.replaceChildren(fragment);
break;
case "alphabetical":
const alpha_fragment = new DocumentFragment();
const alpha_header = document.createElement("h3");
alpha_header.appendChild(document.createTextNode("Alphabetical"));
alpha_fragment.appendChild(alpha_header);
const alpha_list = document.createElement("ul");
alpha_list.classList.add("command-list");
alpha_list.classList.add("command-alpha_list");
sort_command_list(commands);
for(let i = 0; i < commands.length; i++)
alpha_list.appendChild(commands[i]);
alpha_fragment.appendChild(alpha_list);
container.replaceChildren(alpha_fragment);
break;
}
const el_alpha = document.querySelector(".command-ordering-tabs [data-mode=alphabetical]");
const el_cats = document.querySelector(".command-ordering-tabs [data-mode=categorical]");
el_alpha.classList.toggle("active");
el_cats.classList.toggle("active");
}
function do_filter() {
let el_search = document.querySelector("#input-filter");
let el_searchall = document.querySelector("#input-searchall");
@ -88,18 +170,28 @@
window.addEventListener("load", (_event) => {
let el_search = document.querySelector("#input-filter");
let el_searchall = document.querySelector("#input-searchall");
let els_cats = document.querySelectorAll(".command-ordering-tabs button");
el_search.addEventListener("input", do_filter);
el_search.addEventListener("search", do_filter);
el_searchall.addEventListener("change", do_filter);
for(let i = 0; i < els_cats.length; i++) {
els_cats[i].addEventListener("click", handle_display_mode);
els_cats[i].addEventListener("touchend", handle_display_mode);
}
if(localStorage.getItem("commandlist-displaymode") !== null)
set_display_mode(localStorage.getItem("commandlist-displaymode"))
});
</script>
{% for section in sections_help %}
<section class="panel-generic filterable">
<section class="panel-generic filterable" style="--cat-colour: {{ section.category_colour_dark }};">
<h2 id="{{ section.slug }}" class="linked-section-heading">
<a class="section-link" href="#{{ section.slug }}">&#x1f517; <!-- Link Symbol --></a>
<span>{{ section.title }}</span>
<span class="title">{{ section.title }}</span>
<span class="category">{{ section.category }}</span>
</h2>
{{ section.content }}

View File

@ -78,10 +78,18 @@ h1, h2, h3, h4, h5, h6 {
.linked-section-heading > a.section-link:hover {
opacity: 1;
}
.linked-section-heading > span {
.linked-section-heading > span.title {
flex: 1;
word-wrap: anywhere;
}
.linked-section-heading > span.category {
font-size: 0.75em;
border-radius: 1em;
background: var(--cat-colour);
padding: 0 0.5em;
line-height: 1.5;
align-self: flex-start;
}
nav {
background: var(--bg-bright);
@ -353,6 +361,17 @@ footer {
background: var(--pattern-hex), var(--bg-transcluscent);
}
.command-ordering-tabs {
display: flex;
gap: 1em;
margin-bottom: 2em;
}
.command-ordering-tabs button {
flex: 1;
font-size: 1.1em;
padding: 0.5em;
}
.command-ordering-tabs button.active { font-weight: bold; }
.command-list {
margin: 0;
@ -363,11 +382,16 @@ footer {
.command-list > li > a {
text-decoration: none;
}
.command-list.coloured code { background: var(--cat-colour); }
.command-list code {
display: block;
padding: 0.5em;
box-sizing: border-box;
margin: 0.5em;
margin: 0.5em 0;
}
.filterable {
border: 0.2em solid var(--cat-colour);
}

View File

@ -1,7 +1,11 @@
const crypto = require("crypto");
const htmlentities = require("html-entities");
const markdown = require("markdown-it")({
xhtmlOut: true
});
const chroma = require("chroma-js");
const markdown_prism = require("markdown-it-prism");
markdown.use(markdown_prism, {
@ -16,9 +20,19 @@ markdown.use(markdown_prism, {
}
});
function make_section(acc) {
let title = acc[0].match(/#+\s+(.+)\s*/)[1].replace(/^`*|`*$/g, "");
function extract_title(line) {
return line.match(/#+\s+(.+)\s*/)[1].replace(/^`*|`*$/g, "")
}
function make_section(acc, cat_current, cats) {
let title = extract_title(acc[0]);
return {
category: cat_current,
category_colour: cats.get(cat_current),
category_colour_dark: chroma(cats.get(cat_current))
.set("hsl.s", 0.8)
.set("hsl.l", "*0.6")
.css("hsl"),
title: htmlentities.encode(title),
slug: title.toLowerCase().replace(/[^a-z0-9-_\s]+/gi, "")
.replace(/\s+/g, "-")
@ -28,13 +42,36 @@ function make_section(acc) {
}
module.exports = function parse_sections(source) {
const cats = new Map();
source.match(/^##\s+.*$/gm)
.map(extract_title)
.map((item, i, all) => cats.set(
item,
chroma(`hsl(${i/all.length*(360-360/all.length)}, 60%, 90%)`).css("hsl")
));
const lines = source.split(/\r?\n/gi);
const result = [];
let acc = [];
let cat_current = null;
for(let line of lines) {
if(line.startsWith(`#`) && !line.startsWith(`###`)) {
if(acc.length > 0)
result.push(make_section(acc));
if(line.startsWith(`#`)) {
// 1: Deal with the previous section
if(acc.length > 0) {
let heading_level_prev = acc[0].match(/^#+/)[0].length;
if(heading_level_prev === 3 && acc.length > 0) {
result.push(make_section(acc, cat_current, cats));
}
}
// 2: Deal with the new line
let heading_level = line.match(/^#+/)[0].length
if(heading_level === 2)
cat_current = extract_title(line);
acc = [ line ];
}
else
@ -42,7 +79,7 @@ module.exports = function parse_sections(source) {
}
if(acc.length > 0)
result.push(make_section(acc));
result.push(make_section(acc, cat_current, cats));
return result;
return { sections: result, categories: cats };
}

156
.docs/package-lock.json generated
View File

@ -14,6 +14,7 @@
"devDependencies": {
"@11ty/eleventy": "^0.12.1",
"@11ty/eleventy-img": "^0.10.0",
"chroma-js": "^2.1.2",
"markdown-it-prism": "^2.2.1",
"phin": "^3.6.0"
}
@ -729,6 +730,15 @@
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
"dev": true
},
"node_modules/chroma-js": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/chroma-js/-/chroma-js-2.1.2.tgz",
"integrity": "sha512-ri/ouYDWuxfus3UcaMxC1Tfp3IE9K5iQzxc2hSxbBRVNQFut1UuGAsZmiAf2mOUubzGJwgMSv9lHg+XqLaz1QQ==",
"dev": true,
"dependencies": {
"cross-env": "^6.0.3"
}
},
"node_modules/cliui": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
@ -926,6 +936,36 @@
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
"dev": true
},
"node_modules/cross-env": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-6.0.3.tgz",
"integrity": "sha512-+KqxF6LCvfhWvADcDPqo64yVIB31gv/jQulX2NGzKS/g3GEVz6/pt4wjHFtFWsHMddebWD/sDthJemzM4MaAag==",
"dev": true,
"dependencies": {
"cross-spawn": "^7.0.0"
},
"bin": {
"cross-env": "src/bin/cross-env.js",
"cross-env-shell": "src/bin/cross-env-shell.js"
},
"engines": {
"node": ">=8.0"
}
},
"node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
"dev": true,
"dependencies": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
"which": "^2.0.1"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/date-time": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/date-time/-/date-time-0.1.1.tgz",
@ -2180,6 +2220,12 @@
"integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=",
"dev": true
},
"node_modules/isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
"dev": true
},
"node_modules/javascript-stringify": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/javascript-stringify/-/javascript-stringify-2.1.0.tgz",
@ -2959,6 +3005,15 @@
"integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=",
"dev": true
},
"node_modules/path-key": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
"dev": true,
"engines": {
"node": ">=8"
}
},
"node_modules/path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
@ -3821,6 +3876,27 @@
"url": "https://opencollective.com/libvips"
}
},
"node_modules/shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
"dev": true,
"dependencies": {
"shebang-regex": "^3.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/shebang-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
"dev": true,
"engines": {
"node": ">=8"
}
},
"node_modules/short-hash": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/short-hash/-/short-hash-1.0.0.tgz",
@ -4434,6 +4510,21 @@
"node": ">=0.10.0"
}
},
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"dev": true,
"dependencies": {
"isexe": "^2.0.0"
},
"bin": {
"node-which": "bin/node-which"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/which-module": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
@ -5180,6 +5271,15 @@
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
"dev": true
},
"chroma-js": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/chroma-js/-/chroma-js-2.1.2.tgz",
"integrity": "sha512-ri/ouYDWuxfus3UcaMxC1Tfp3IE9K5iQzxc2hSxbBRVNQFut1UuGAsZmiAf2mOUubzGJwgMSv9lHg+XqLaz1QQ==",
"dev": true,
"requires": {
"cross-env": "^6.0.3"
}
},
"cliui": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
@ -5357,6 +5457,26 @@
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
"dev": true
},
"cross-env": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-6.0.3.tgz",
"integrity": "sha512-+KqxF6LCvfhWvADcDPqo64yVIB31gv/jQulX2NGzKS/g3GEVz6/pt4wjHFtFWsHMddebWD/sDthJemzM4MaAag==",
"dev": true,
"requires": {
"cross-spawn": "^7.0.0"
}
},
"cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
"dev": true,
"requires": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
"which": "^2.0.1"
}
},
"date-time": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/date-time/-/date-time-0.1.1.tgz",
@ -6335,6 +6455,12 @@
"integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=",
"dev": true
},
"isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
"dev": true
},
"javascript-stringify": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/javascript-stringify/-/javascript-stringify-2.1.0.tgz",
@ -6930,6 +7056,12 @@
"integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=",
"dev": true
},
"path-key": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
"dev": true
},
"path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
@ -7656,6 +7788,21 @@
"tunnel-agent": "^0.6.0"
}
},
"shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
"dev": true,
"requires": {
"shebang-regex": "^3.0.0"
}
},
"shebang-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
"dev": true
},
"short-hash": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/short-hash/-/short-hash-1.0.0.tgz",
@ -8149,6 +8296,15 @@
"integrity": "sha1-YU9/v42AHwu18GYfWy9XhXUOTwk=",
"dev": true
},
"which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"dev": true,
"requires": {
"isexe": "^2.0.0"
}
},
"which-module": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",

View File

@ -22,6 +22,7 @@
"devDependencies": {
"@11ty/eleventy": "^0.12.1",
"@11ty/eleventy-img": "^0.10.0",
"chroma-js": "^2.1.2",
"markdown-it-prism": "^2.2.1",
"phin": "^3.6.0"
},