Merge branch 'main' of github.com:Theodor-Springmann-Stiftung/musenalm

This commit is contained in:
Simon Martens
2025-03-05 12:36:15 +01:00
17 changed files with 82 additions and 51 deletions

View File

@@ -28,33 +28,8 @@ Modelle umwandeln (zzt RecordProxy)
- Ersellen & Abfragen FTS5-Tabellen - Ersellen & Abfragen FTS5-Tabellen
- Erstellen Textseiten - Erstellen Textseiten
- Technologie-Stack auf Server-Rendering / Go Templates umgestellt
- Die Seiten sollten jetzt insgesamt schneller laden
- Man kann auf der Startseite und in der Suche nach Almanach-Nummern suchen
- Überall werden die Almanachnummer und Inhaltsnummer angezeigt
- Die URL referenziert die Almanachnummern, nicht mher die Datenbank-IDs
- In der Almanach-Ansicht werden die Abkürzungen erklärt
- In der Almanach- und Suchansicht werden Sammlungen abgehoben
- In der Almanach- und Suchansicht werden auch mehrere Bilder zu einem Eintrag angezeigt, falls vorhanden
- In der Almanach- und Suchansicht kann nach Inhalten frei gefiltert werden, oder nach Typ
- Es gibt URLs, die fest verlinkt werden können für einzelne:
- Personen
- Reihen
- Bände
- Beiträge
- Alle Suchanfragen
- Die Suche ist klar nach Typ unterteilt und insgesamt zuverlässiger
- Zusätzlich zur jetzigen Suchfunktion gibt es für Beiträge und Bände noch eine Detailsuche
- Suchergebnisse können nach Typ, Person, Jahr gefiltert werden
- Suchergebnisse könnnen nach Jahr und Band, nach Band und Jahr (nach Personen) sortiert werden
TODO danach: TODO danach:
- Google-Suchoptimierung
- Error Pages prüfen & error-Verhalten von HTMX - Error Pages prüfen & error-Verhalten von HTMX
- Weißraum in den Antworten - Weißraum in den Antworten
- Antworten komprimieren - Antworten komprimieren

View File

@@ -3,7 +3,7 @@ var x = (r) => {
}; };
var _ = (r, t, e) => t.has(r) || x("Cannot " + e); var _ = (r, t, e) => t.has(r) || x("Cannot " + e);
var c = (r, t, e) => (_(r, t, "read from private field"), e ? e.call(r) : t.get(r)), n = (r, t, e) => t.has(r) ? x("Cannot add the same private member more than once") : t instanceof WeakSet ? t.add(r) : t.set(r, e), u = (r, t, e, i) => (_(r, t, "write to private field"), i ? i.call(r, e) : t.set(r, e), e), g = (r, t, e) => (_(r, t, "access private method"), e); var c = (r, t, e) => (_(r, t, "read from private field"), e ? e.call(r) : t.get(r)), n = (r, t, e) => t.has(r) ? x("Cannot add the same private member more than once") : t instanceof WeakSet ? t.add(r) : t.set(r, e), u = (r, t, e, i) => (_(r, t, "write to private field"), i ? i.call(r, e) : t.set(r, e), e), g = (r, t, e) => (_(r, t, "access private method"), e);
const S = "script[xslt-onload]", w = "xslt-template", k = "xslt-transformed", M = "filter-list", m = "filter-list-list", C = "filter-list-item", I = "filter-list-input", y = "filter-list-searchable", B = "scroll-button", q = "tool-tip", P = "abbrev-tooltips", R = "int-link", H = "popup-image", $ = "tab-list", N = "filter-pill", F = "image-reel"; const S = "script[xslt-onload]", w = "xslt-template", k = "xslt-transformed", M = "filter-list", m = "filter-list-list", C = "filter-list-item", I = "filter-list-input", y = "filter-list-searchable", B = "scroll-button", q = "tool-tip", P = "abbrev-tooltips", H = "int-link", R = "popup-image", $ = "tab-list", N = "filter-pill", F = "image-reel";
var d, b, E; var d, b, E;
class V { class V {
constructor() { constructor() {
@@ -77,7 +77,7 @@ class O extends HTMLElement {
} }
render() { render() {
this.innerHTML = ` this.innerHTML = `
<a href="${this.getURL()}" class="!no-underline block text-base" hx-target="#searchresults" hx-select="#searchresults" hx-swap="outerHTML show:window:top"> <a href="${this.getURL()}" class="!no-underline block text-base" hx-target="#searchresults" hx-select="#searchresults" hx-indicator="body" hx-swap="outerHTML show:window:top">
<div class="flex flex-row filter-pill rounded-lg bg-orange-100 hover:saturate-50 px-2.5"> <div class="flex flex-row filter-pill rounded-lg bg-orange-100 hover:saturate-50 px-2.5">
${this.renderIcon()} ${this.renderIcon()}
<div class="flex flex-row filter-pill-label-value !items-baseline text-slate-700"> <div class="flex flex-row filter-pill-label-value !items-baseline text-slate-700">
@@ -119,7 +119,7 @@ class U extends HTMLElement {
return ["data-url"]; return ["data-url"];
} }
set items(e) { set items(e) {
Array.isArray(e) && (this._items = e, this.render()), htmx && htmx.process(this); Array.isArray(e) && (this._items = e, this.render());
} }
get items() { get items() {
return this._items; return this._items;
@@ -216,7 +216,7 @@ class U extends HTMLElement {
${this.Input()} ${this.Input()}
${this.List()} ${this.List()}
</div> </div>
`; `, htmx && htmx.process(this);
} }
ActiveDot(e) { ActiveDot(e) {
return g(this, f, L).call(this, e), ""; return g(this, f, L).call(this, e), "";
@@ -252,6 +252,10 @@ class U extends HTMLElement {
(i, s) => ` (i, s) => `
<a <a
href="${this.getURL(i)}" href="${this.getURL(i)}"
hx-indicator="body"
hx-swap="outerHTML show:none"
hx-select="main"
hx-target="main"
class="${C} block px-2.5 py-0.5 hover:bg-slate-200 no-underline ${s % 2 === 0 ? "bg-stone-100" : "bg-stone-50"}" class="${C} block px-2.5 py-0.5 hover:bg-slate-200 no-underline ${s % 2 === 0 ? "bg-stone-100" : "bg-stone-50"}"
${g(this, f, L).call(this, i) ? 'aria-current="page"' : ""}> ${g(this, f, L).call(this, i) ? 'aria-current="page"' : ""}>
${this.ActiveDot(i)} ${this.ActiveDot(i)}
@@ -741,12 +745,12 @@ class X extends HTMLElement {
} }
} }
v = new WeakMap(); v = new WeakMap();
customElements.define(R, K); customElements.define(H, K);
customElements.define(P, p); customElements.define(P, p);
customElements.define(M, U); customElements.define(M, U);
customElements.define(B, D); customElements.define(B, D);
customElements.define(q, z); customElements.define(q, z);
customElements.define(H, G); customElements.define(R, G);
customElements.define($, j); customElements.define($, j);
customElements.define(N, O); customElements.define(N, O);
customElements.define(F, X); customElements.define(F, X);

File diff suppressed because one or more lines are too long

View File

@@ -43,7 +43,8 @@
</script> </script>
</head> </head>
<body class="w-full h-full min-h-full" hx-ext="response-targets" hx-boost="true"> <body class="w-full min-h-full" hx-ext="response-targets" hx-boost="true">
<div class="pb-12">
{{ block "body" . }} {{ block "body" . }}
<!-- Default app body... --> <!-- Default app body... -->
{{ end }} {{ end }}

View File

@@ -3,8 +3,8 @@
Musenalm &ndash; {{ .result.Entry.PreferredTitle }}, Musenalm &ndash; {{ .result.Entry.PreferredTitle }},
{{ if .result.Content.TitleStmt -}} {{ if .result.Content.TitleStmt -}}
{{ .result.Content.TitleStmt }} {{ .result.Content.TitleStmt }}
{{ else if .result.Content.Incipit -}} {{ else if .result.Content.IncipitStmt -}}
{{ .result.Content.Incipit }} {{ .result.Content.IncipitStmt }}
{{ else -}} {{ else -}}
Nr. Nr.
{{ .result.Content.MusenalmID }} {{ .result.Content.MusenalmID }}
@@ -15,8 +15,8 @@
content="Almanach: {{ .result.Entry.PreferredTitle }}, content="Almanach: {{ .result.Entry.PreferredTitle }},
{{ if .result.Content.TitleStmt -}} {{ if .result.Content.TitleStmt -}}
{{ .result.Content.TitleStmt }} {{ .result.Content.TitleStmt }}
{{ else if .result.Content.Incipit -}} {{ else if .result.Content.IncipitStmt -}}
{{ .result.Content.Incipit }} {{ .result.Content.IncipitStmt }}
{{ else -}} {{ else -}}
Nr. Nr.
{{ .result.Content.MusenalmID }} {{ .result.Content.MusenalmID }}

View File

@@ -1,5 +1,5 @@
{{- $model := . -}} {{- $model := . -}}
<div id="intropageroot"> <div id="intropageroot" class="!mb-12">
<image-reel class="hidden lg:block max-w-full my-8 mx-12 relative" id="imagecontainer"> <image-reel class="hidden lg:block max-w-full my-8 mx-12 relative" id="imagecontainer">
<div class="overflow-hidden flex flex-row justify-between"> <div class="overflow-hidden flex flex-row justify-between">
{{- range $i, $img := $model.bilder -}} {{- range $i, $img := $model.bilder -}}
@@ -22,8 +22,8 @@
</div> </div>
</image-reel> </image-reel>
<div class="w-full min-h-full mt-8"> <div class="w-full min-h-full mt-8 mb-12">
<div class="text-center relative max-w-screen-xl mx-auto" data-=""> <div class="text-center relative max-w-screen-xl mx-auto">
<img <img
src="/assets/musen.png" src="/assets/musen.png"
class="max-w-[28rem] mx-auto lg:absolute left-2/3 top-2" class="max-w-[28rem] mx-auto lg:absolute left-2/3 top-2"
@@ -43,7 +43,7 @@
{{- Safe $model.texte.Abs2 -}} {{- Safe $model.texte.Abs2 -}}
</div> </div>
</div> </div>
<div class="text-center mt-8 startlinks font-serif text-xl" data-=""> <div class="text-center mt-8 startlinks font-serif text-xl">
<a href="/redaktion/einfuehrung" class="">Einleitung</a> <a href="/redaktion/einfuehrung" class="">Einleitung</a>
<div class="inline px-1">|</div> <div class="inline px-1">|</div>
<a href="/reihen" class="font-bold">Alle Bände<i class="ri-arrow-right-double-line"></i></a> <a href="/reihen" class="font-bold">Alle Bände<i class="ri-arrow-right-double-line"></i></a>

View File

@@ -15,7 +15,7 @@
placeholder="Suchbegriff" placeholder="Suchbegriff"
x-model="search" x-model="search"
hx-get="" hx-get=""
hx-trigger="input[if: this.value.length >= 3] delay:3000ms, keyup[enter, if: this.value.length >= 2]" hx-trigger="input delay:1000ms, keyup[enter]"
hx-select="#searchresults" hx-select="#searchresults"
hx-target="#searchresults" hx-target="#searchresults"
hx-swap="outerHTML" hx-swap="outerHTML"

View File

@@ -5,7 +5,7 @@
<div class="text "> <div class="text ">
{{ if .record.Title }}<h1 class="mb-12">{{ .record.Title }}</h1>{{ end }} {{ if .record.Title }}<h1 class="mb-12">{{ .record.Title }}</h1>{{ end }}
<div class="flex flex-row gap-x-6 justify-between "> <div class="flex flex-row gap-x-6 justify-between ">
<div class="grow shrink-0 text indented"> <div class="grow shrink-0 text indented jumptext">
{{ Safe .record.Text }} {{ Safe .record.Text }}
</div> </div>
<div> <div>

View File

@@ -11,12 +11,16 @@
:class="search ? 'inactive' : 'active'" :class="search ? 'inactive' : 'active'"
href="?letter={{ $r }}" href="?letter={{ $r }}"
{{ if eq $model.active $r }}aria-current="page"{{ end }} {{ if eq $model.active $r }}aria-current="page"{{ end }}
hx-indicator="body"
hx-select="main" hx-select="main"
hx-target="main" hx-target="main"
hx-swap="outerHTML scroll:#pageheading:top" hx-swap="outerHTML show:none"
>{{ $r }}</a >{{ $r }}</a
> >
{{ end }} {{ end }}
<i class="ml-2 pb-1 ri-hourglass-2-fill request-indicator spinning"></i>
</div> </div>
<div class="flex-grow"></div> <div class="flex-grow"></div>

View File

@@ -75,6 +75,7 @@
id="simplesearchform" id="simplesearchform"
class="w-full font-serif" class="w-full font-serif"
method="get" method="get"
hx-indicator="body"
action="/suche/baende" action="/suche/baende"
autocomplete="off"> autocomplete="off">
{{- if not $model.parameters.Extended -}} {{- if not $model.parameters.Extended -}}
@@ -212,6 +213,10 @@
<div class="container-normal" id="searchresults"> <div class="container-normal" id="searchresults">
<div class="border-b border-zinc-300 flex flex-row justify-between"> <div class="border-b border-zinc-300 flex flex-row justify-between">
<div> <div>
<div class="inline-block">
<i class="ri-hourglass-2-fill request-indicator spinning"></i>
</div>
<div class="request-indicator">&middot;</div>
{{ if $model.parameters.Query -}} {{ if $model.parameters.Query -}}
Suche nach <b>»{{ $model.parameters.Query }}«</b> &middot; Suche nach <b>»{{ $model.parameters.Query }}«</b> &middot;
{{- end -}} {{- end -}}
@@ -247,6 +252,7 @@
name="sort" name="sort"
id="sort" id="sort"
hx-get="{{- $model.parameters.ToQueryParamsBaende -}}" hx-get="{{- $model.parameters.ToQueryParamsBaende -}}"
hx-indicator="body"
trigger="change" trigger="change"
hx-push-url="true" hx-push-url="true"
hx-select="main" hx-select="main"

View File

@@ -76,6 +76,7 @@
id="simplesearchform" id="simplesearchform"
class="w-full font-serif" class="w-full font-serif"
method="get" method="get"
hx-indicator="body"
action="/suche/beitraege" action="/suche/beitraege"
autocomplete="off"> autocomplete="off">
{{- if not $model.parameters.Extended -}} {{- if not $model.parameters.Extended -}}

View File

@@ -4,6 +4,7 @@
{{ if gt $model.parameters.Page 1 -}} {{ if gt $model.parameters.Page 1 -}}
<a <a
href="{{- $model.parameters.ToQueryParamsBeitraege -}}&page={{ $model.parameters.Prev }}" href="{{- $model.parameters.ToQueryParamsBeitraege -}}&page={{ $model.parameters.Prev }}"
hx-indicator="body"
class="mr-1.5 text-stone-500 hover:text-slate-900"> class="mr-1.5 text-stone-500 hover:text-slate-900">
<i class="ri-arrow-left-long-line"></i> <i class="ri-arrow-left-long-line"></i>
</a> </a>
@@ -13,6 +14,7 @@
{{ if lt $model.parameters.Page ($model.result.PagesCount) -}} {{ if lt $model.parameters.Page ($model.result.PagesCount) -}}
<a <a
href="{{- $model.parameters.ToQueryParamsBeitraege -}}&page={{ $model.parameters.Next }}" href="{{- $model.parameters.ToQueryParamsBeitraege -}}&page={{ $model.parameters.Next }}"
hx-indicator="body"
class="ml-1.5 text-stone-500 hover:text-slate-900"> class="ml-1.5 text-stone-500 hover:text-slate-900">
<i class="ri-arrow-right-long-line"></i> <i class="ri-arrow-right-long-line"></i>
</a> </a>

View File

@@ -21,6 +21,8 @@
<div class="border-b border-zinc-300 flex flex-row justify-between"> <div class="border-b border-zinc-300 flex flex-row justify-between">
<div class="flex flex-row gap-x-2"> <div class="flex flex-row gap-x-2">
<div><i class="ri-hourglass-2-fill request-indicator spinning"></i></div>
<div class="request-indicator">&middot;</div>
{{ if $model.parameters.Query -}} {{ if $model.parameters.Query -}}
<div>Suche nach <b>»{{ $model.parameters.Query }}«</b></div> <div>Suche nach <b>»{{ $model.parameters.Query }}«</b></div>
<div>&middot;</div> <div>&middot;</div>
@@ -69,6 +71,7 @@
hx-select="#searchresults" hx-select="#searchresults"
hx-target="#searchresults" hx-target="#searchresults"
hx-swap="outerHTML" hx-swap="outerHTML"
hx-indicator="body"
{{ if $model.filters.OnlyScans -}}checked{{- end -}} /> {{ if $model.filters.OnlyScans -}}checked{{- end -}} />
<label <label
@@ -87,6 +90,7 @@
trigger="change" trigger="change"
hx-push-url="true" hx-push-url="true"
hx-select="#subresults" hx-select="#subresults"
hx-indicator="body"
hx-target="#subresults" hx-target="#subresults"
hx-swap="outerHTML show:window:top"> hx-swap="outerHTML show:window:top">
<option <option

View File

@@ -1,11 +1,11 @@
{{ $extendable := . }} {{ $extendable := . }}
<div class="col-span-6 flex flex-row text-stone-700 gap-x-2"> <div class="flex flex-row text-stone-700 gap-x-2">
<div> <div>
<i class="ri-information-2-fill"></i> <i class="ri-information-2-fill"></i>
</div> </div>
<div class="font-sans hyphens-auto text-sm pt-1"> <div class="font-sans hyphens-auto text-sm pt-1 max-w-[48rem]">
Die Suche durchsucht ganze Datensätze nach dem Vorkommen aller eingegebenen Suchbegriffe. Felder Die Suche durchsucht ganze Datensätze nach dem Vorkommen aller eingegebenen Suchbegriffe. Felder
können oben einzeln aus der Suche ausgeschlossen werden. Auch partielle Treffer in Worten werden können oben einzeln aus der Suche ausgeschlossen werden. Auch partielle Treffer in Worten werden
angezeigt. Wörter mit weniger als drei Zeichen, Sonderzeichen &ndash; auch Satzzeichen &ndash; angezeigt. Wörter mit weniger als drei Zeichen, Sonderzeichen &ndash; auch Satzzeichen &ndash;

View File

@@ -15,7 +15,9 @@
placeholder="Suchbegriff (min. 3 Zeichen)" placeholder="Suchbegriff (min. 3 Zeichen)"
class="w-full grow class="w-full grow
placeholder:italic font-serif placeholder:font-sans" /> placeholder:italic font-serif placeholder:font-sans" />
<button id="submitbutton" type="submit" class="min-w-36" form="simplesearchform">Suchen</button> <button id="submitbutton" type="submit" class="min-w-36" form="simplesearchform">
<i class="ri-hourglass-2-fill request-indicator spinning mr-1"></i>Suchen
</button>
</div> </div>
{{ if $extendable }} {{ if $extendable }}

View File

@@ -180,7 +180,7 @@ class FilterPill extends HTMLElement {
render() { render() {
this.innerHTML = ` this.innerHTML = `
<a href="${this.getURL()}" class="!no-underline block text-base" hx-target="#searchresults" hx-select="#searchresults" hx-swap="outerHTML show:window:top"> <a href="${this.getURL()}" class="!no-underline block text-base" hx-target="#searchresults" hx-select="#searchresults" hx-indicator="body" hx-swap="outerHTML show:window:top">
<div class="flex flex-row filter-pill rounded-lg bg-orange-100 hover:saturate-50 px-2.5"> <div class="flex flex-row filter-pill rounded-lg bg-orange-100 hover:saturate-50 px-2.5">
${this.renderIcon()} ${this.renderIcon()}
<div class="flex flex-row filter-pill-label-value !items-baseline text-slate-700"> <div class="flex flex-row filter-pill-label-value !items-baseline text-slate-700">
@@ -242,8 +242,6 @@ class FilterList extends HTMLElement {
this._items = data; this._items = data;
this.render(); this.render();
} }
if (!htmx) return;
htmx.process(this);
} }
get items() { get items() {
@@ -451,6 +449,8 @@ class FilterList extends HTMLElement {
${this.List()} ${this.List()}
</div> </div>
`; `;
if (!htmx) return;
htmx.process(this);
} }
ActiveDot(item) { ActiveDot(item) {
@@ -505,6 +505,10 @@ class FilterList extends HTMLElement {
(item, index) => ` (item, index) => `
<a <a
href="${this.getURL(item)}" href="${this.getURL(item)}"
hx-indicator="body"
hx-swap="outerHTML show:none"
hx-select="main"
hx-target="main"
class="${FILTER_LIST_ITEM} block px-2.5 py-0.5 hover:bg-slate-200 no-underline ${ class="${FILTER_LIST_ITEM} block px-2.5 py-0.5 hover:bg-slate-200 no-underline ${
index % 2 === 0 ? "bg-stone-100" : "bg-stone-50" index % 2 === 0 ? "bg-stone-100" : "bg-stone-50"
}" }"

View File

@@ -484,7 +484,35 @@
/*direction: rtl;*/ /*direction: rtl;*/
} }
body .request-indicator {
@apply !hidden;
}
.spinning {
animation: spin 1s ease-out infinite;
}
body.htmx-request #simplesearchform #sumbmitbutton {
@apply cursor-wait pointer-events-none;
}
body.htmx-request .request-indicator {
@apply !inline-block;
}
.tab-list-head[aria-pressed="true"] { .tab-list-head[aria-pressed="true"] {
@apply !text-slate-900 bg-stone-50; @apply !text-slate-900 bg-stone-50;
} }
@keyframes spin {
0% {
transform: rotate(0deg);
}
80% {
transform: rotate(360deg);
} /* Most rotation happens early */
100% {
transform: rotate(360deg);
} /* Pause at the final position */
}
} }