Add rudimentary support for automatic breadcrumbs — currently only with ActionName as labels
This commit is contained in:
parent
b80ce89fe8
commit
47f0214fe3
44
package-lock.json
generated
44
package-lock.json
generated
@ -16,7 +16,7 @@
|
|||||||
"@sealcode/file-manager": "^1.0.2",
|
"@sealcode/file-manager": "^1.0.2",
|
||||||
"@sealcode/jdd": "^0.8.3",
|
"@sealcode/jdd": "^0.8.3",
|
||||||
"@sealcode/jdd-editor": "^0.2.9",
|
"@sealcode/jdd-editor": "^0.2.9",
|
||||||
"@sealcode/sealgen": "^0.19.6",
|
"@sealcode/sealgen": "^0.19.17",
|
||||||
"@sealcode/show-first-row": "^0.1.0",
|
"@sealcode/show-first-row": "^0.1.0",
|
||||||
"@sealcode/simplemde": "^1.12.1",
|
"@sealcode/simplemde": "^1.12.1",
|
||||||
"@sealcode/sortable": "^0.0.1",
|
"@sealcode/sortable": "^0.0.1",
|
||||||
@ -1342,9 +1342,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sealcode/file-manager": {
|
"node_modules/@sealcode/file-manager": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@sealcode/file-manager/-/file-manager-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@sealcode/file-manager/-/file-manager-1.0.3.tgz",
|
||||||
"integrity": "sha512-BOMgC90QffE9cVFKkLVTjDbUJ5WB9YqcmS4fwqFxgnnC3YlH9xb9rff3iGXSkKOHm0kCeSjq0Ohasxtq/z72WQ==",
|
"integrity": "sha512-S2E1Yh7an8i9LEnX1FNvGXZwiHeWmg4dwXhMZR62xaRP7JjUve/2630aIYdiL7Ci6WLOsI7XsPhEQDlbQvQTDA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/mime-types": "^2.1.4",
|
"@types/mime-types": "^2.1.4",
|
||||||
"@types/uuid": "^9.0.8",
|
"@types/uuid": "^9.0.8",
|
||||||
@ -1462,9 +1462,9 @@
|
|||||||
"integrity": "sha512-pDsGlk2KokQkwzsJDBUWJFDRpEoxxth6TMQGDJyCTmWnd1Vn+cQb5moXDKaf7cXnWb9Y6QtdNX/fPzM/3RH2Cg=="
|
"integrity": "sha512-pDsGlk2KokQkwzsJDBUWJFDRpEoxxth6TMQGDJyCTmWnd1Vn+cQb5moXDKaf7cXnWb9Y6QtdNX/fPzM/3RH2Cg=="
|
||||||
},
|
},
|
||||||
"node_modules/@sealcode/sealgen": {
|
"node_modules/@sealcode/sealgen": {
|
||||||
"version": "0.19.6",
|
"version": "0.19.17",
|
||||||
"resolved": "https://registry.npmjs.org/@sealcode/sealgen/-/sealgen-0.19.6.tgz",
|
"resolved": "https://registry.npmjs.org/@sealcode/sealgen/-/sealgen-0.19.17.tgz",
|
||||||
"integrity": "sha512-dUccXEGSTlZrkrzkta2cObQUDBzbTrfYLZkbZer6E9nyYKB/7Jbj03R+C3wGxfwvrsewGfHiar+v8JHjOiguyQ==",
|
"integrity": "sha512-QDhtLhgEwRDgaLx2efTZZII1jq5PGLVUlGOF6S4vIcSl4jA9UGrljPbw0JE5Opk8LY1oZE/Xkjz5rz8tsSFe3A==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@koa/router": "^12.0.1",
|
"@koa/router": "^12.0.1",
|
||||||
"@sealcode/file-manager": "^1.0.2",
|
"@sealcode/file-manager": "^1.0.2",
|
||||||
@ -1500,7 +1500,7 @@
|
|||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"koa": "^2.13.0",
|
"koa": "^2.13.0",
|
||||||
"koa-responsive-image-router": "^0.2.24",
|
"koa-responsive-image-router": "^0.2.24",
|
||||||
"sealious": "^0.21.20",
|
"sealious": "^0.21.36",
|
||||||
"stimulus": "^3.2.2"
|
"stimulus": "^3.2.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -12093,12 +12093,12 @@
|
|||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/sealious": {
|
"node_modules/sealious": {
|
||||||
"version": "0.21.30",
|
"version": "0.21.36",
|
||||||
"resolved": "https://registry.npmjs.org/sealious/-/sealious-0.21.30.tgz",
|
"resolved": "https://registry.npmjs.org/sealious/-/sealious-0.21.36.tgz",
|
||||||
"integrity": "sha512-EWmqqFpDdgMvBmqAnaoX3FN2TmWdEgA1MgCUoQNB0iwUSl5m2Ghpd3W3eoM4H0Nr6bTr1ogJaOsAZJJFlIdc0Q==",
|
"integrity": "sha512-K1Gm7nM/fg8AEnd9QAJBSpoF6qJSuuwNlWjpCvz69sJkQ9FR8N3uRoX5t+gOPu7xqmBsrlNOx7JyF3sg3WQsFQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@koa/router": "^12.0.1",
|
"@koa/router": "^12.0.1",
|
||||||
"@sealcode/file-manager": "^1.0.1",
|
"@sealcode/file-manager": "^1.0.3",
|
||||||
"@sealcode/ts-predicates": "^0.4.3",
|
"@sealcode/ts-predicates": "^0.4.3",
|
||||||
"@types/boom": "^7.3.0",
|
"@types/boom": "^7.3.0",
|
||||||
"@types/clone": "^0.1.30",
|
"@types/clone": "^0.1.30",
|
||||||
@ -15954,9 +15954,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@sealcode/file-manager": {
|
"@sealcode/file-manager": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@sealcode/file-manager/-/file-manager-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@sealcode/file-manager/-/file-manager-1.0.3.tgz",
|
||||||
"integrity": "sha512-BOMgC90QffE9cVFKkLVTjDbUJ5WB9YqcmS4fwqFxgnnC3YlH9xb9rff3iGXSkKOHm0kCeSjq0Ohasxtq/z72WQ==",
|
"integrity": "sha512-S2E1Yh7an8i9LEnX1FNvGXZwiHeWmg4dwXhMZR62xaRP7JjUve/2630aIYdiL7Ci6WLOsI7XsPhEQDlbQvQTDA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/mime-types": "^2.1.4",
|
"@types/mime-types": "^2.1.4",
|
||||||
"@types/uuid": "^9.0.8",
|
"@types/uuid": "^9.0.8",
|
||||||
@ -16061,9 +16061,9 @@
|
|||||||
"integrity": "sha512-pDsGlk2KokQkwzsJDBUWJFDRpEoxxth6TMQGDJyCTmWnd1Vn+cQb5moXDKaf7cXnWb9Y6QtdNX/fPzM/3RH2Cg=="
|
"integrity": "sha512-pDsGlk2KokQkwzsJDBUWJFDRpEoxxth6TMQGDJyCTmWnd1Vn+cQb5moXDKaf7cXnWb9Y6QtdNX/fPzM/3RH2Cg=="
|
||||||
},
|
},
|
||||||
"@sealcode/sealgen": {
|
"@sealcode/sealgen": {
|
||||||
"version": "0.19.6",
|
"version": "0.19.17",
|
||||||
"resolved": "https://registry.npmjs.org/@sealcode/sealgen/-/sealgen-0.19.6.tgz",
|
"resolved": "https://registry.npmjs.org/@sealcode/sealgen/-/sealgen-0.19.17.tgz",
|
||||||
"integrity": "sha512-dUccXEGSTlZrkrzkta2cObQUDBzbTrfYLZkbZer6E9nyYKB/7Jbj03R+C3wGxfwvrsewGfHiar+v8JHjOiguyQ==",
|
"integrity": "sha512-QDhtLhgEwRDgaLx2efTZZII1jq5PGLVUlGOF6S4vIcSl4jA9UGrljPbw0JE5Opk8LY1oZE/Xkjz5rz8tsSFe3A==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@koa/router": "^12.0.1",
|
"@koa/router": "^12.0.1",
|
||||||
"@sealcode/file-manager": "^1.0.2",
|
"@sealcode/file-manager": "^1.0.2",
|
||||||
@ -24023,12 +24023,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sealious": {
|
"sealious": {
|
||||||
"version": "0.21.30",
|
"version": "0.21.36",
|
||||||
"resolved": "https://registry.npmjs.org/sealious/-/sealious-0.21.30.tgz",
|
"resolved": "https://registry.npmjs.org/sealious/-/sealious-0.21.36.tgz",
|
||||||
"integrity": "sha512-EWmqqFpDdgMvBmqAnaoX3FN2TmWdEgA1MgCUoQNB0iwUSl5m2Ghpd3W3eoM4H0Nr6bTr1ogJaOsAZJJFlIdc0Q==",
|
"integrity": "sha512-K1Gm7nM/fg8AEnd9QAJBSpoF6qJSuuwNlWjpCvz69sJkQ9FR8N3uRoX5t+gOPu7xqmBsrlNOx7JyF3sg3WQsFQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@koa/router": "^12.0.1",
|
"@koa/router": "^12.0.1",
|
||||||
"@sealcode/file-manager": "^1.0.1",
|
"@sealcode/file-manager": "^1.0.3",
|
||||||
"@sealcode/ts-predicates": "^0.4.3",
|
"@sealcode/ts-predicates": "^0.4.3",
|
||||||
"@types/boom": "^7.3.0",
|
"@types/boom": "^7.3.0",
|
||||||
"@types/clone": "^0.1.30",
|
"@types/clone": "^0.1.30",
|
||||||
|
|||||||
@ -95,7 +95,7 @@
|
|||||||
"@sealcode/file-manager": "^1.0.2",
|
"@sealcode/file-manager": "^1.0.2",
|
||||||
"@sealcode/jdd": "^0.8.3",
|
"@sealcode/jdd": "^0.8.3",
|
||||||
"@sealcode/jdd-editor": "^0.2.9",
|
"@sealcode/jdd-editor": "^0.2.9",
|
||||||
"@sealcode/sealgen": "^0.19.6",
|
"@sealcode/sealgen": "^0.19.17",
|
||||||
"@sealcode/show-first-row": "^0.1.0",
|
"@sealcode/show-first-row": "^0.1.0",
|
||||||
"@sealcode/simplemde": "^1.12.1",
|
"@sealcode/simplemde": "^1.12.1",
|
||||||
"@sealcode/sortable": "^0.0.1",
|
"@sealcode/sortable": "^0.0.1",
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import type { HTMLArgs, HTMLOptions } from "@sealcode/sealgen";
|
import type { HTMLArgs, HTMLOptions } from "@sealcode/sealgen";
|
||||||
import { tempstreamAsync } from "tempstream";
|
|
||||||
import type { Readable } from "stream";
|
|
||||||
import { toKebabCase } from "js-convert-case";
|
import { toKebabCase } from "js-convert-case";
|
||||||
|
import type { Readable } from "stream";
|
||||||
|
import { tempstreamAsync } from "tempstream";
|
||||||
import { DEFAULT_HTML_LANG } from "./config.js";
|
import { DEFAULT_HTML_LANG } from "./config.js";
|
||||||
import { default_navbar } from "./routes/common/navbar.js";
|
|
||||||
import { defaultHead } from "./defaultHead.js";
|
import { defaultHead } from "./defaultHead.js";
|
||||||
|
import { default_navbar } from "./routes/common/navbar.js";
|
||||||
|
|
||||||
const default_html_options: Partial<HTMLOptions> = {
|
const default_html_options: Partial<HTMLOptions> = {
|
||||||
showFooter: true,
|
showFooter: true,
|
||||||
|
|||||||
56
src/back/routes/common/breadcrumbs.css
Normal file
56
src/back/routes/common/breadcrumbs.css
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
.breadcrumbs {
|
||||||
|
--bg: var(--color-brand-accent);
|
||||||
|
display: flex;
|
||||||
|
--height: 2rem;
|
||||||
|
--padding: 0px 16px;
|
||||||
|
flex-flow: row wrap;
|
||||||
|
align-items: center;
|
||||||
|
row-gap: 8px;
|
||||||
|
|
||||||
|
span {
|
||||||
|
padding: var(--padding);
|
||||||
|
}
|
||||||
|
|
||||||
|
a,
|
||||||
|
& > span {
|
||||||
|
display: inline-block;
|
||||||
|
line-height: var(--height);
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row nowrap;
|
||||||
|
color: var(--color-brand-text-on-accent);
|
||||||
|
|
||||||
|
span {
|
||||||
|
background-color: var(--bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrow-right,
|
||||||
|
.arrow-left {
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrow-right {
|
||||||
|
border-top: calc(var(--height) / 2) solid transparent;
|
||||||
|
border-bottom: calc(var(--height) / 2) solid transparent;
|
||||||
|
|
||||||
|
border-left: calc(var(--height) / 2) solid var(--bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrow-left {
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-top: calc(var(--height) / 2) solid var(--bg);
|
||||||
|
border-bottom: calc(var(--height) / 2) solid var(--bg);
|
||||||
|
border-left: calc(var(--height) / 2) solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
filter: brightness(1.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
71
src/back/routes/common/breadcrumbs.tsx
Normal file
71
src/back/routes/common/breadcrumbs.tsx
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import type { Context } from "koa";
|
||||||
|
import { TempstreamJSX } from "tempstream";
|
||||||
|
import { get_breadcrumbs_from_path } from "../url-tree.js";
|
||||||
|
|
||||||
|
const arrow_width = 8;
|
||||||
|
const arrow_height = 28;
|
||||||
|
|
||||||
|
function arrow_tail() {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width={`${arrow_width}px`}
|
||||||
|
height={`${arrow_height}px`}
|
||||||
|
viewBox="0 0 ${arrow_width} ${arrow_height}"
|
||||||
|
style="margin-right: -1px"
|
||||||
|
>
|
||||||
|
{
|
||||||
|
/* HTML */ `<path
|
||||||
|
d="M 0 0 ${arrow_width} 0l0 ${arrow_height} -${arrow_width} 0L${arrow_width} ${arrow_height /
|
||||||
|
2}Z"
|
||||||
|
style="fill:var(--bg);"
|
||||||
|
/>`
|
||||||
|
}
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function arrow_head() {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width={`${arrow_width}px`}
|
||||||
|
height={`${arrow_height}px`}
|
||||||
|
viewBox="0 0 ${arrow_width} ${arrow_height}"
|
||||||
|
>
|
||||||
|
{
|
||||||
|
/* HTML */ `<path
|
||||||
|
d="m 0 0 ${arrow_width} ${arrow_height / 2}L0 ${arrow_height}Z"
|
||||||
|
style="fill:var(--bg);"
|
||||||
|
/>`
|
||||||
|
}
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function breadcrumbs(ctx: Context) {
|
||||||
|
return (
|
||||||
|
<div class="breadcrumbs">
|
||||||
|
{get_breadcrumbs_from_path(ctx.path)
|
||||||
|
.map((e, i, all) => {
|
||||||
|
if (!e.actionName) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
if (i == all.length - 1) {
|
||||||
|
return <span>{e.actionName}</span>;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<a href={e.url}>
|
||||||
|
{i !== 0 ? arrow_tail() : ""}
|
||||||
|
<span>{e.actionName}</span>
|
||||||
|
{arrow_head()}
|
||||||
|
</a>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.filter((e) => e !== "")
|
||||||
|
.join("")}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -1,9 +1,10 @@
|
|||||||
import type { BaseContext } from "koa";
|
import type { Context } from "koa";
|
||||||
import { button } from "src/back/elements/button.js";
|
import { button } from "src/back/elements/button.js";
|
||||||
import { tempstream } from "tempstream";
|
import { tempstream } from "tempstream";
|
||||||
import type { FlatTemplatable } from "tempstream";
|
import type { FlatTemplatable } from "tempstream";
|
||||||
|
import { breadcrumbs } from "./breadcrumbs.js";
|
||||||
|
|
||||||
export async function default_navbar(ctx: BaseContext): Promise<FlatTemplatable> {
|
export async function default_navbar(ctx: Context): Promise<FlatTemplatable> {
|
||||||
const { items: navbar_items } = await ctx.$app.collections["navbar-links"]
|
const { items: navbar_items } = await ctx.$app.collections["navbar-links"]
|
||||||
.list(ctx.$context)
|
.list(ctx.$context)
|
||||||
.fetch();
|
.fetch();
|
||||||
@ -25,18 +26,19 @@ export async function default_navbar(ctx: BaseContext): Promise<FlatTemplatable>
|
|||||||
)
|
)
|
||||||
.join("\n");
|
.join("\n");
|
||||||
|
|
||||||
return /* HTML */ `<nav>
|
return /* HTML */ tempstream`<nav>
|
||||||
<a href="/" class="nav-logo">
|
<a href="/" class="nav-logo">
|
||||||
<img
|
<img
|
||||||
src="/assets/logo"
|
src="/assets/logo"
|
||||||
alt="${ctx.$app.manifest.name} - logo"
|
alt="${ctx.$app.manifest.name} - logo"
|
||||||
width="50"
|
width="50"
|
||||||
height="50"
|
height="50"
|
||||||
/>
|
/>
|
||||||
${ctx.$app.manifest.name}
|
${ctx.$app.manifest.name}
|
||||||
</a>
|
</a>
|
||||||
<ul>
|
<ul>
|
||||||
${linksHTML}
|
${linksHTML}
|
||||||
</ul>
|
</ul>
|
||||||
</nav>`;
|
</nav>
|
||||||
|
${breadcrumbs(ctx)} `;
|
||||||
}
|
}
|
||||||
|
|||||||
24
src/back/routes/url-tree.ts
Normal file
24
src/back/routes/url-tree.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { url_tree } from "./routes.js";
|
||||||
|
import type { URLTree } from "./routes.js";
|
||||||
|
|
||||||
|
export function get_breadcrumbs_from_path(url: string) {
|
||||||
|
let position: URLTree = url_tree;
|
||||||
|
const elements = url.split("/").filter((e) => e != "");
|
||||||
|
const breadcrumbs: { actionName?: string; url?: string }[] = [
|
||||||
|
{ actionName: "Home", url: "/" },
|
||||||
|
];
|
||||||
|
let path_so_far = "";
|
||||||
|
for (const element of elements) {
|
||||||
|
if (position.children[element]) {
|
||||||
|
position = position.children[element];
|
||||||
|
} else if (Object.keys(position.children).find((e) => e.startsWith(":"))) {
|
||||||
|
const key = Object.keys(position.children).find((e) => e.startsWith(":"));
|
||||||
|
position = position.children[key!]!;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
path_so_far += "/" + element;
|
||||||
|
breadcrumbs.push({ actionName: position.actionName, url: path_so_far });
|
||||||
|
}
|
||||||
|
return breadcrumbs;
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user