From caaf86f90d8083bded5d6ac66e847e8f32ab157a Mon Sep 17 00:00:00 2001 From: Simon Martens Date: Thu, 27 Feb 2025 21:05:34 +0100 Subject: [PATCH] Beitragsansicht --- dbmodels/collectionhelper.go | 126 +++++++++++ dbmodels/content.go | 8 + dbmodels/sorting.go | 7 + helpers/datatypes/float64.go | 15 ++ helpers/functions/string.go | 16 ++ pages/almanach.go | 38 +++- pages/contents.go | 10 + templating/engine.go | 1 + views/routes/almanach/body.gohtml | 200 +++--------------- .../almanach/components/entrydata.gohtml | 188 ++++++++++++++++ views/routes/components/_content.gohtml | 118 +++++++++++ views/routes/components/_contentlist.gohtml | 23 ++ views/transform/main.js | 84 ++++++++ views/transform/site.css | 33 +++ 14 files changed, 692 insertions(+), 175 deletions(-) create mode 100644 dbmodels/collectionhelper.go create mode 100644 helpers/datatypes/float64.go create mode 100644 pages/contents.go create mode 100644 views/routes/almanach/components/entrydata.gohtml create mode 100644 views/routes/components/_content.gohtml create mode 100644 views/routes/components/_contentlist.gohtml diff --git a/dbmodels/collectionhelper.go b/dbmodels/collectionhelper.go new file mode 100644 index 0000000..b379523 --- /dev/null +++ b/dbmodels/collectionhelper.go @@ -0,0 +1,126 @@ +package dbmodels + +import ( + "encoding/json" + "regexp" + "sort" + "strconv" + "strings" +) + +// CollectionInfo holds only the ID, a list of single references, and the Recorded flag. +type CollectionInfo struct { + Collection *Content + Singles []int + Recorded bool +} + +func (ci CollectionInfo) String() string { + marshalled, _ := json.Marshal(ci) + return string(marshalled) +} + +// parseAnnotation detects "nicht erfasst" references (Recorded=false), +// then finds all "INr" references (both single values and ranges). +// Ranges like "100-105" are fully expanded to singles. Duplicates are removed. +// Any references not in `inos` are ignored. +func ParseAnnotation(c *Content, annotation string, inos []int) CollectionInfo { + ci := CollectionInfo{ + Collection: c, + Singles: []int{}, + Recorded: true, // Default + } + + // 1) Detect phrases like "nicht erfasst", "nicht aufgenommen", etc. + notRecordedPatterns := []string{"erfasst", "aufgenommen", "verzeichnet", "registriert"} + lowerAnn := strings.ToLower(annotation) + if strings.Contains(lowerAnn, "nicht") { + for _, kw := range notRecordedPatterns { + if strings.Contains(lowerAnn, kw) { + ci.Recorded = false + break + } + } + } + + // We'll keep singles in a map for deduplication + singlesMap := make(map[int]struct{}) + + // 2) Regex that matches "INr" plus the numeric portion (including dash / punctuation). + re := regexp.MustCompile(`(?i)\bINr[.:]?\s+([\d,\-\s–—;/.]+)`) + matches := re.FindAllStringSubmatch(annotation, -1) + + // Regex to unify different dash characters into a simple '-' + dashRegex := regexp.MustCompile(`[–—−‒]`) + + // Helper to expand a range, e.g. 10615–10621 => 10615..10621 + expandRange := func(fromVal, toVal int) { + // If reversed, its a typo + if fromVal > toVal { + return + } + for v := fromVal; v <= toVal; v++ { + if inList(v, inos) { + singlesMap[v] = struct{}{} + } + } + } + + for _, m := range matches { + numericChunk := m[1] + + // Replace typographic dashes with ASCII hyphen + numericChunk = dashRegex.ReplaceAllString(numericChunk, "-") + + // Also unify semicolons or slashes to commas + extraDelims := regexp.MustCompile(`[;/]+`) + numericChunk = extraDelims.ReplaceAllString(numericChunk, ",") + + // Now split on commas + parts := strings.Split(numericChunk, ",") + for _, p := range parts { + p = strings.TrimSpace(p) + if p == "" { + continue + } + // If we see a hyphen, treat it as a range + if strings.Contains(p, "-") { + rangeParts := strings.SplitN(p, "-", 2) + if len(rangeParts) == 2 { + fromStr := strings.TrimSpace(rangeParts[0]) + toStr := strings.TrimSpace(rangeParts[1]) + if fromVal, errFrom := strconv.Atoi(fromStr); errFrom == nil { + if toVal, errTo := strconv.Atoi(toStr); errTo == nil { + expandRange(fromVal, toVal) + } + } + } + } else { + // Single integer reference + if val, err := strconv.Atoi(p); err == nil { + if inList(val, inos) { + singlesMap[val] = struct{}{} + } + } + } + } + } + + // Flatten the map into a sorted slice + for s := range singlesMap { + ci.Singles = append(ci.Singles, s) + } + sort.Ints(ci.Singles) + + return ci +} + +// inList checks membership in `inos` +func inList(x int, list []int) bool { + for _, item := range list { + if item == x { + return true + } + } + return false +} diff --git a/dbmodels/content.go b/dbmodels/content.go index 3fe9d8a..077616e 100644 --- a/dbmodels/content.go +++ b/dbmodels/content.go @@ -157,6 +157,14 @@ func (c *Content) SetScans(scans []*filesystem.File) { c.Set(SCAN_FIELD, scans) } +func (r *Content) ImagePaths() []string { + ret := []string{} + for _, s := range r.Scans() { + ret = append(ret, "/api/files/"+r.TableName()+"/"+r.Id+"/"+s) + } + return ret +} + func (c *Content) Numbering() float64 { return c.GetFloat(NUMBERING_FIELD) } diff --git a/dbmodels/sorting.go b/dbmodels/sorting.go index 3f876f3..ec19712 100644 --- a/dbmodels/sorting.go +++ b/dbmodels/sorting.go @@ -3,6 +3,7 @@ package dbmodels import ( "slices" + "github.com/Theodor-Springmann-Stiftung/musenalm/helpers/datatypes" "golang.org/x/text/collate" "golang.org/x/text/language" ) @@ -41,3 +42,9 @@ func Sort_REntriesSeries_Year(entries []*REntriesSeries, entriesMap map[string]* return ientry.Year() - jentry.Year() }) } + +func Sort_Contents_Numbering(contents []*Content) { + slices.SortFunc(contents, func(i, j *Content) int { + return datatypes.CompareFloat(i.Numbering(), j.Numbering()) + }) +} diff --git a/helpers/datatypes/float64.go b/helpers/datatypes/float64.go new file mode 100644 index 0000000..0214136 --- /dev/null +++ b/helpers/datatypes/float64.go @@ -0,0 +1,15 @@ +package datatypes + +import "math" + +const float64EqualityThreshold = 1e-9 + +func CompareFloat(a, b float64) int { + if math.Abs(a-b) < float64EqualityThreshold { + return 0 + } + if a < b { + return -1 + } + return 1 +} diff --git a/helpers/functions/string.go b/helpers/functions/string.go index b5ac6fb..045101f 100644 --- a/helpers/functions/string.go +++ b/helpers/functions/string.go @@ -1,13 +1,17 @@ package functions import ( + "fmt" "html/template" + "regexp" "strings" "golang.org/x/text/cases" "golang.org/x/text/language" ) +var linksexp = regexp.MustCompile(`INr\s*([0-9]+)(?:\s*[-,;]\s*[0-9]*)*\s*(?:,|;)?\s*(?:obj|Obj)?\s*[0-9]*(?:\s*[-,;]\s*[0-9]*)*`) + func Safe(s string) template.HTML { if len(s) == 0 { return "" @@ -39,3 +43,15 @@ func First(s string) string { return string(r[0]) } + +func LinksAnnotation(s string) string { + annotation := linksexp.ReplaceAllStringFunc(s, func(match string) string { + submatches := linksexp.FindStringSubmatch(match) + if len(submatches) > 1 { + return fmt.Sprintf(`%s`, submatches[1], match) + } + return match + }) + + return annotation +} diff --git a/pages/almanach.go b/pages/almanach.go index dae3dfd..1dcf78f 100644 --- a/pages/almanach.go +++ b/pages/almanach.go @@ -72,6 +72,9 @@ type AlmanachResult struct { EntriesSeries map[string]*dbmodels.REntriesSeries // <- Key is series id EntriesAgents []*dbmodels.REntriesAgents ContentsAgents map[string][]*dbmodels.RContentsAgents // <- Key is content id + + CInfoByCollection map[string]dbmodels.CollectionInfo + CInfoByContent map[int][]dbmodels.CollectionInfo } func NewAlmanachResult(app core.App, id string) (*AlmanachResult, error) { @@ -110,6 +113,8 @@ func NewAlmanachResult(app core.App, id string) (*AlmanachResult, error) { return nil, err } + dbmodels.Sort_Contents_Numbering(contents) + contentsagents, err := dbmodels.RContentsAgents_Contents(app, dbmodels.Ids(contents)) caids := []any{} caMap := map[string][]*dbmodels.RContentsAgents{} @@ -137,7 +142,7 @@ func NewAlmanachResult(app core.App, id string) (*AlmanachResult, error) { agentsMap[a.Id] = a } - return &AlmanachResult{ + ret := &AlmanachResult{ Entry: entry, Places: places, Series: series, @@ -146,6 +151,35 @@ func NewAlmanachResult(app core.App, id string) (*AlmanachResult, error) { EntriesSeries: srelationsMap, EntriesAgents: entriesagents, ContentsAgents: caMap, - }, nil + } + + ret.Collections() + return ret, nil } + +func (r *AlmanachResult) Collections() { + ids := []int{} + collections := []*dbmodels.Content{} + for _, s := range r.Contents { + ids = append(ids, s.MusenalmID()) + for _, t := range s.MusenalmType() { + if t == "Sammlung" { + collections = append(collections, s) + } + } + } + + ccontentcollectionmap := map[int][]dbmodels.CollectionInfo{} + ccollectioncontentmap := map[string]dbmodels.CollectionInfo{} + for _, v := range collections { + cinfo := dbmodels.ParseAnnotation(v, v.Annotation(), ids) + ccollectioncontentmap[v.Id] = cinfo + for _, c := range cinfo.Singles { + ccontentcollectionmap[c] = append(ccontentcollectionmap[c], cinfo) + } + } + + r.CInfoByCollection = ccollectioncontentmap + r.CInfoByContent = ccontentcollectionmap +} diff --git a/pages/contents.go b/pages/contents.go new file mode 100644 index 0000000..5904a63 --- /dev/null +++ b/pages/contents.go @@ -0,0 +1,10 @@ +package pages + +type ContentListFilterParams struct { + Agent string + Type string + Page int + OnlyScans bool + AlmNumber int + Entry string +} diff --git a/templating/engine.go b/templating/engine.go index 350fdad..e116610 100644 --- a/templating/engine.go +++ b/templating/engine.go @@ -130,6 +130,7 @@ func (e *Engine) funcs() error { e.AddFunc("First", functions.First) e.AddFunc("ReplaceSlashParen", functions.ReplaceSlashParen) e.AddFunc("ReplaceSlashParenSlash", functions.ReplaceSlashParenSlash) + e.AddFunc("LinksAnnotation", functions.LinksAnnotation) // Time & Date Functions e.AddFunc("Today", functions.Today) diff --git a/views/routes/almanach/body.gohtml b/views/routes/almanach/body.gohtml index 9761068..5fd423c 100644 --- a/views/routes/almanach/body.gohtml +++ b/views/routes/almanach/body.gohtml @@ -2,6 +2,8 @@ {{/* .result: type AlmanachResult struct { Entry *dbmodels.Entry + Agent *dbmodels.Agent + Entries map[string]*dbmodels.Entry Places []*dbmodels.Place Series []*dbmodels.Series Contents []*dbmodels.Content @@ -9,11 +11,23 @@ EntriesSeries map[string]*dbmodels.REntriesSeries // <- Key is series id EntriesAgents []*dbmodels.REntriesAgents ContentsAgents map[string][]*dbmodels.RContentsAgents // <- Key is content id + + + CInfoByCollection map[string]*dbmodels.CollectionInfo + CInfoByContent map[int][]*dbmodels.CollectionInfo } -*/}} -{{ $isGer := false }} -{{ $isFra := false }} -{{ $isEng := false }} + + + .parameters { + Sort string + MusenalmID string + PersonFilter string + TitleFilter string + EntryFilter string + TypeFilter []string + Scanfilter bool + } + */}} -
-
Almanach
-
-
-
-
-
-
-
Almanach-Nummer
-
{{ $model.result.Entry.MusenalmID }}
-
- {{- if $model.result.Entry.PreferredTitle -}} -
-
Kurztitel
-
{{ $model.result.Entry.PreferredTitle }}
-
- {{- end -}} - {{- if $model.result.Entry.TitleStmt -}} -
-
Titel
-
{{ $model.result.Entry.TitleStmt }}
-
- {{- end -}} -
-
Jahr
-
- {{- if $model.result.Entry.Year -}} - {{ $model.result.Entry.Year }} - {{- else -}} - [keine Angabe] - {{- end -}} -
-
- {{- if $model.result.Entry.ResponsibilityStmt -}} -
-
Herausgeberangabe
-
{{ $model.result.Entry.ResponsibilityStmt }}
-
- {{- end -}} - {{- if $model.result.Entry.Extent -}} -
-
Umfang
-
- -
-
- {{- end -}} - {{- if $model.result.Entry.Language -}} -
-
Sprache
-
- {{- range $i, $lang := $model.result.Entry.Language -}} - {{- if $i -}},{{- end -}} - {{- if eq $lang "ger" -}} - {{ $isGer = true }} - Deutsch - {{- else if eq $lang "eng" -}} - {{ $isEng = true }} - Englisch - {{- else if eq $lang "fre" -}} - {{ $isFra = true }} - Französisch - {{- else if eq $lang "ita" -}} - Italienisch - {{- else if eq $lang "lat" -}} - Latein - {{- else -}} - {{ $lang }} - {{- end -}} - {{- end -}} -
-
- {{- end -}} - {{- if $model.result.Entry.References -}} -
-
Nachweise
-
- {{- $model.result.Entry.References -}} -
-
- {{- end -}} - {{- if $model.result.Series -}} -
-
Reihen
-
- {{- range $i, $s := $model.result.Series -}} -
- {{- $rel := index $model.result.EntriesSeries $s.Id -}} - {{- if $rel -}} - {{- if not (eq $rel.Type "Bevorzugter Reihentitel") -}} - - {{- if eq $rel.Type "Früherer Reihentitel" -}} - hat Titelauflage s.a. - {{- else if eq $rel.Type "Späterer Reihentitel" -}} - ist Titelauflage von, s.a. - {{- else if eq $rel.Type "In anderer Sprache" -}} - {{- if $isFra -}} - In deutscher Sprache s.a. - {{- else -}} - In französischer Sprache s.a. - {{- end -}} - {{- else if eq $rel.Type "Alternatives Titelblatt" -}} - alternatives Titelblatt, s.a. - {{- end -}} - - {{- end -}} - {{- end -}} - {{ $s.Title }} -
- {{- end -}} -
-
- {{- end -}} - {{- if $model.result.Places -}} -
-
Orte
-
- {{- range $i, $p := $model.result.Places -}} - - {{- end -}} -
-
- {{- end -}} - {{- if $model.result.EntriesAgents -}} -
-
Personen
-
- {{- range $i, $r := $model.result.EntriesAgents -}} - {{- $a := index $model.result.Agents $r.Agent -}} - {{- if $a -}} -
- - {{ $a.Name }} - - - {{- $r.Type -}} - -
- {{- end -}} - {{- end -}} -
-
- {{- end -}} - {{- if $model.result.Entry.Annotation -}} -
-
Anmerkungen
-
- {{- Safe (ReplaceSlashParen $model.result.Entry.Annotation) -}} -
-
- {{- end -}} -
-
-
-
+{{ template "entrydata" $model }} - +
+ {{- range $i, $c := $model.result.Contents -}} + {{- $rels := index $model.result.ContentsAgents $c.Id -}} + {{- $coll := index $model.result.CInfoByContent $c.MusenalmID -}} + {{- if and $coll (index $coll 0) -}} + {{- end -}} + {{- template "_content" Arr $c $model.result.Entry $rels $model.result.Agents -}} + {{- end -}}
diff --git a/views/routes/almanach/components/entrydata.gohtml b/views/routes/almanach/components/entrydata.gohtml new file mode 100644 index 0000000..f9222c7 --- /dev/null +++ b/views/routes/almanach/components/entrydata.gohtml @@ -0,0 +1,188 @@ +{{ $model := . }} +{{/* .result: + type AlmanachResult struct { + Entry *dbmodels.Entry + Places []*dbmodels.Place + Series []*dbmodels.Series + Contents []*dbmodels.Content + Agents map[string]*dbmodels.Agent // <- Key is agent id + EntriesSeries map[string]*dbmodels.REntriesSeries // <- Key is series id + EntriesAgents []*dbmodels.REntriesAgents + ContentsAgents map[string][]*dbmodels.RContentsAgents // <- Key is content id + } +*/}} +{{ $isGer := false }} +{{ $isFra := false }} +{{ $isEng := false }} + + +
+
Almanach
+
+
+
+
+
+
Almanach-Nummer
+
{{ $model.result.Entry.MusenalmID }}
+
+ {{- if $model.result.Entry.PreferredTitle -}} +
+
Kurztitel
+
{{ $model.result.Entry.PreferredTitle }}
+
+ {{- end -}} + {{- if $model.result.Entry.TitleStmt -}} +
+
Titel
+
{{ $model.result.Entry.TitleStmt }}
+
+ {{- end -}} +
+
Jahr
+
+ {{- if $model.result.Entry.Year -}} + {{ $model.result.Entry.Year }} + {{- else -}} + [keine Angabe] + {{- end -}} +
+
+ {{- if $model.result.Entry.ResponsibilityStmt -}} +
+
Herausgeberangabe
+
{{ $model.result.Entry.ResponsibilityStmt }}
+
+ {{- end -}} + {{- if $model.result.Entry.Extent -}} +
+
Umfang
+
+ +
+
+ {{- end -}} + {{- if $model.result.Entry.Language -}} +
+
Sprache
+
+ {{- range $i, $lang := $model.result.Entry.Language -}} + {{- if $i -}},{{- end -}} + {{- if eq $lang "ger" -}} + {{ $isGer = true }} + Deutsch + {{- else if eq $lang "eng" -}} + {{ $isEng = true }} + Englisch + {{- else if eq $lang "fre" -}} + {{ $isFra = true }} + Französisch + {{- else if eq $lang "ita" -}} + Italienisch + {{- else if eq $lang "lat" -}} + Latein + {{- else -}} + {{ $lang }} + {{- end -}} + {{- end -}} +
+
+ {{- end -}} + {{- if $model.result.Entry.References -}} +
+
Nachweise
+
+ {{- $model.result.Entry.References -}} +
+
+ {{- end -}} + {{- if $model.result.Series -}} +
+
Reihen
+
+ {{- range $i, $s := $model.result.Series -}} +
+ {{- $rel := index $model.result.EntriesSeries $s.Id -}} + {{- if $rel -}} + {{- if not (eq $rel.Type "Bevorzugter Reihentitel") -}} + + {{- if eq $rel.Type "Früherer Reihentitel" -}} + hat Titelauflage s.a. + {{- else if eq $rel.Type "Späterer Reihentitel" -}} + ist Titelauflage von, s.a. + {{- else if eq $rel.Type "In anderer Sprache" -}} + {{- if $isFra -}} + In deutscher Sprache s.a. + {{- else -}} + In französischer Sprache s.a. + {{- end -}} + {{- else if eq $rel.Type "Alternatives Titelblatt" -}} + alternatives Titelblatt, s.a. + {{- end -}} + + {{- end -}} + {{- end -}} + {{ $s.Title }} +
+ {{- end -}} +
+
+ {{- end -}} + {{- if $model.result.Places -}} +
+
Orte
+
+ {{- range $i, $p := $model.result.Places -}} + + {{- end -}} +
+
+ {{- end -}} + {{- if $model.result.EntriesAgents -}} +
+
Personen
+
+ {{- range $i, $r := $model.result.EntriesAgents -}} + {{- $a := index $model.result.Agents $r.Agent -}} + {{- if $a -}} +
+ + {{ $a.Name }} + + + {{- $r.Type -}} + +
+ {{- end -}} + {{- end -}} +
+
+ {{- end -}} + {{- if $model.result.Entry.Annotation -}} +
+
Anmerkungen
+
+ {{- Safe (ReplaceSlashParen $model.result.Entry.Annotation) -}} +
+
+ {{- end -}} +
+
+
+
+ + diff --git a/views/routes/components/_content.gohtml b/views/routes/components/_content.gohtml new file mode 100644 index 0000000..8be7baa --- /dev/null +++ b/views/routes/components/_content.gohtml @@ -0,0 +1,118 @@ +{{ $model := . }} +{{/* .1 - *Content + .2 - *Entry + .3 - []*RContentsAgents + .4 - map[string]*Agent +*/}} + +{{- $content := index . 0 -}} +{{- $entry := index . 1 -}} +{{- $rcas := index . 2 -}} +{{- $agents := index . 3 -}} + + +
+
+ {{- if $content.Extent -}} +
+ S. + {{- $content.Extent -}} +
+ {{- end -}} + {{- if $content.MusenalmType -}} +
+ {{- range $_, $t := $content.MusenalmType -}} +
+ {{- $t -}} +
+ {{- end -}} +
+ {{- end -}} + +
+ +
+
+ {{- if $content.TitleStmt -}} +
Titel
+
{{- $content.TitleStmt -}}
+ {{- end -}} + {{- if $content.IncipitStmt -}} +
Incipit
+
{{ $content.IncipitStmt }}…
+ {{- end -}} + {{- if $content.ResponsibilityStmt -}} +
Autorangabe
+
{{- $content.ResponsibilityStmt -}}
+ {{- end -}} + {{- if $rcas -}} +
Personen
+
+ {{- range $_, $rca := $rcas -}} +
+ {{- $agent := index $agents $rca.Agent -}} + {{- if $agent -}} +
+ {{- $agent.Name -}} + ({{ $agent.BiographicalData -}}) +
+ {{- end -}} +
+ {{- end -}} +
+ {{- end -}} + {{- if $content.Annotation -}} +
Anmerkung
+
+ {{- Safe (LinksAnnotation (ReplaceSlashParen + $content.Annotation)) + -}} +
+ {{- end -}} +
+
+ + {{- $scans := $content.ImagePaths -}} + {{- $slen := len $scans -}} + {{- $double := false -}} + {{- if gt $slen 2 -}} + {{- $double = true -}} + {{- end -}} +
+ {{- if $scans -}} +
+ {{- range $_, $scan := $scans -}} +
+ + + +
+ {{- end -}} +
+ {{- end -}} +
+ +
+
+ NR + {{ $content.MusenalmID -}} +
+ +
+
diff --git a/views/routes/components/_contentlist.gohtml b/views/routes/components/_contentlist.gohtml new file mode 100644 index 0000000..bbf0abc --- /dev/null +++ b/views/routes/components/_contentlist.gohtml @@ -0,0 +1,23 @@ +{{ $model := . }} +{{/* .result: + type AlmanachResult struct { + Entry *dbmodels.Entry + Places []*dbmodels.Place + Series []*dbmodels.Series + Contents []*dbmodels.Content + Agents map[string]*dbmodels.Agent // <- Key is agent id + EntriesSeries map[string]*dbmodels.REntriesSeries // <- Key is series id + EntriesAgents []*dbmodels.REntriesAgents + ContentsAgents map[string][]*dbmodels.RContentsAgents // <- Key is content id + } + + .parameters { + Sort string + MusenalmID string + PersonFilter string + TitleFilter string + EntryFilter string + TypeFilter []string + Scanfilter bool + } + */}} diff --git a/views/transform/main.js b/views/transform/main.js index d3deab8..cded9f3 100644 --- a/views/transform/main.js +++ b/views/transform/main.js @@ -15,6 +15,7 @@ const SCROLL_BUTTON_ELEMENT = "scroll-button"; const TOOLTIP_ELEMENT = "tool-tip"; const ABBREV_TOOLTIPS_ELEMENT = "abbrev-tooltips"; const INT_LINK_ELEMENT = "int-link"; +const POPUP_IMAGE_ELEMENT = "popup-image"; class XSLTParseProcess { #processors; @@ -567,6 +568,88 @@ class ToolTip extends HTMLElement { } } +class PopupImage extends HTMLElement { + constructor() { + super(); + this.overlay = null; + this._preview = null; + this._description = null; + this._imageURL = ""; + } + + connectedCallback() { + this._imageURL = this.getAttribute("data-image-url") || ""; + this._preview = this.querySelector("img"); + this._description = this.querySelector(".image-description"); + + if (this._preview) { + this._preview.addEventListener("click", () => { + this.showOverlay(); + }); + } + } + + disconnectedCallback() { + // Optionally remove the overlay if the element is removed from the DOM + if (this.overlay && this.overlay.parentNode) { + this.overlay.parentNode.removeChild(this.overlay); + } + } + + showOverlay() { + const descriptionHtml = this._description ? this._description.innerHTML : ""; + this.overlay = document.createElement("div"); + this.overlay.classList.add( + "fixed", + "inset-0", + "z-50", + "bg-black/70", + "flex", + "items-center", + "justify-center", + "p-4", + ); + + this.overlay.innerHTML = ` +
+ + + Popup Image + +
+ ${descriptionHtml} +
+
+ `; + + const closeButton = this.overlay.querySelector("button"); + if (closeButton) { + closeButton.addEventListener("click", () => { + this.hideOverlay(); + }); + } + + this.overlay.addEventListener("click", (evt) => { + if (evt.target === this.overlay) { + this.hideOverlay(); + } + }); + + document.body.appendChild(this.overlay); + } + + hideOverlay() { + this.overlay.parentNode.removeChild(this.overlay); + this.overlay = null; + } +} + class AbbreviationTooltips extends HTMLElement { static get observedAttributes() { return ["data-text", "data-abbrevmap"]; @@ -772,5 +855,6 @@ customElements.define(ABBREV_TOOLTIPS_ELEMENT, AbbreviationTooltips); customElements.define(FILTER_LIST_ELEMENT, FilterList); customElements.define(SCROLL_BUTTON_ELEMENT, ScrollButton); customElements.define(TOOLTIP_ELEMENT, ToolTip); +customElements.define("popup-image", PopupImage); export { XSLTParseProcess, FilterList, ScrollButton, AbbreviationTooltips }; diff --git a/views/transform/site.css b/views/transform/site.css index acfb547..219c1d7 100644 --- a/views/transform/site.css +++ b/views/transform/site.css @@ -406,4 +406,37 @@ #extendedsearchcolumn label { @apply col-span-3 bg-stone-200 align-middle px-2.5 text-slate-900 items-center flex text-base; } + + .content .fields { + @apply grid grid-cols-10 gap-y-0.5 w-full gap-x-4; + } + + .content .fieldlabel { + @apply col-span-1 font-bold whitespace-nowrap grow-0 shrink-0 font-sans text-sm align-baseline mt-1 text-right; + } + + .content .fieldvalue { + @apply col-span-9 font-serif text-left grow align-top max-w-[60ch]; + } + + .content { + @apply text-base; + } + + #almanachcontents .content { + @apply border-b-8 border-stone-50; + } + + #almanachcontents .columnone { + @apply bg-stone-50 pt-2 pr-3 mr-1; + } + + #almanachcontents .columntwo { + @apply bg-stone-100 py-4 pl-6 ml-1; + } + + #almanachcontents .columnthree { + @apply bg-stone-100 pr-6 py-4; + /*direction: rtl;*/ + } }