diff --git a/.forgejo/workflows/update_publications_data.yaml b/.forgejo/workflows/update_publication_pages.yaml
similarity index 52%
rename from .forgejo/workflows/update_publications_data.yaml
rename to .forgejo/workflows/update_publication_pages.yaml
index c6ef4c8..d8720c5 100644
--- a/.forgejo/workflows/update_publications_data.yaml
+++ b/.forgejo/workflows/update_publication_pages.yaml
@@ -16,19 +16,11 @@ jobs:
uses: actions/checkout@v4
- name: Prepare environment
uses: ./.forgejo/actions/prep-metadata-query
- - name: Install jq
- run: |
- apt-get update
- apt-get install -y jq
- - name: Update pages
+ - name: Update publication pages
run: |
dtc get-records ${{ env.DUMPTHINGS_APIURL }} public -C XYZPublication \
| qri inline-records -p about -p attributed_to -c public \
- | jq -sc '{pid: "xyzri:this-is-not-important", publications: .}' \
- | qri render-record page_templates/publications.json.j2 'data/publications.json'
+ | qri render-record page_templates/publication.md.j2 \
+ 'content/{__pid_curie_reference}/_index.md'
- name: Deposit changes
- run: |
- git add data
- git diff --quiet --cached \
- && echo "Already up-to-date" \
- || ( git commit -m "chore: auto-generate content from metadata" && git push origin )
+ uses: ./.forgejo/actions/deposit-changes
diff --git a/content/publications/_index.md b/content/publications/_index.md
deleted file mode 100644
index 7c38345..0000000
--- a/content/publications/_index.md
+++ /dev/null
@@ -1,3 +0,0 @@
----
-title: Publications
----
\ No newline at end of file
diff --git a/layouts/_partials/publication-item.html b/layouts/_partials/publication-item.html
new file mode 100644
index 0000000..d4fe990
--- /dev/null
+++ b/layouts/_partials/publication-item.html
@@ -0,0 +1,67 @@
+
+
+
+
+
+ {{ .Title }}
+
+ {{ if .Params.date }}
+ {{ $year := substr .Params.date 0 4 }}
+
+ ({{ $year }})
+
+ {{ end }}
+ {{ with .Params.doi }}
+
+
+ DOI: {{ . }}
+
+
+ {{ end }}
+ {{ with .Params.kind }}
+
+ {{ . | replaceRE "^.*:" "" }}
+
+ {{ end }}
+
+
+
+ {{ with .Params.author }}
+
+ {{ range $i, $a := . }}
+
+ {{ $a.given_name }} {{ $a.family_name }}
+
+ {{ end }}
+
+ {{ end }}
+
+
+ {{ with .Params.topic }}
+
+ {{ range . }}
+
+ {{ .display_label }}
+
+ {{ end }}
+
+ {{ end }}
+
+
\ No newline at end of file
diff --git a/layouts/publications/list.html b/layouts/publications/taxonomy.html
similarity index 68%
rename from layouts/publications/list.html
rename to layouts/publications/taxonomy.html
index 4dea629..9199550 100644
--- a/layouts/publications/list.html
+++ b/layouts/publications/taxonomy.html
@@ -9,6 +9,9 @@
+
+ Clear all
+
Kind
@@ -26,14 +29,17 @@
-
-
+
+
+ {{ $pubPages := where .Site.Pages "Section" "publications" }}
+ {{ $pubPagesTerms := where $pubPages "Kind" "term" }}
+
+ {{ range $i, $p := $pubPagesTerms }}
+ {{ partial "publication-item.html" $p }}
+ {{ end }}
+
-
-
diff --git a/page_templates/publication.md.j2 b/page_templates/publication.md.j2
new file mode 100644
index 0000000..cbfb886
--- /dev/null
+++ b/page_templates/publication.md.j2
@@ -0,0 +1,59 @@
+{% macro taxonomy_terms(taxonomy, terms_list) -%}
+{% if terms_list -%}
+{{ taxonomy }}:
+{% for el in terms_list -%}
+{% if el.pid -%}
+ - {{ el.pid.split('/')[-1] }}
+{% endif -%}
+{% endfor -%}
+{% endif -%}
+{%- endmacro -%}
+
+{%- set doi = (
+ __rec.identifiers
+ | selectattr('schema_type', 'equalto', 'dlthings:DOI')
+ | list
+ | first
+) if __rec.identifiers is defined and __rec.identifiers is sequence else none -%}
+
+{%- set generation = (
+ __rec.generated_by
+ | selectattr('object', 'equalto', 'obo:IAO_0000444')
+ | list
+ | first
+) if __rec.generated_by is defined and __rec.generated_by is sequence else none -%}
+
+{%- set authors = [] -%}
+{%- for auth in __rec.attributed_to -%}
+ {%- set _ = authors.append({
+ "pid": auth.object.pid | default(none),
+ "given_name": auth.object.given_name | default(none),
+ "family_name": auth.object.family_name | default(none)
+ }) -%}
+{%- endfor -%}
+
+{%- set topics = [] -%}
+{%- for top in __rec.about -%}
+ {%- set _ = topics.append({
+ "pid": top.pid | default(none),
+ "display_label": top.display_label | default(none)
+ }) -%}
+{%- endfor -%}
+
+{%- set publication = {
+ "pid": __rec.pid | default(none),
+ "doi": doi.notation | default(none) if doi else none,
+ "date": generation.at_time | default(none) if generation else none,
+ "title": __rec.title | default(none),
+ "kind": __rec.kind | default(none),
+ "author": authors | default(none),
+ "topic": topics | default(none)
+} -%}
+---
+title: {{ publication.title | toyaml | replace('...\n','') | trim }}
+{{ taxonomy_terms('persons', authors) -}}
+{{ taxonomy_terms('topics', topics) -}}
+params:
+ graphRootNodePID: {{ pid }}
+ {{ publication | toyaml | indent(2) | trim }}
+---
diff --git a/static/publications.css b/static/publications.css
index 58e1cd5..8c6ef31 100644
--- a/static/publications.css
+++ b/static/publications.css
@@ -18,6 +18,12 @@
max-height: calc(100vh - 4rem);
overflow-y: auto;
scroll-behavior: smooth;
+ display: flex;
+ flex-direction: column;
+}
+
+.clear-button {
+ margin-left: auto;
}
.results-panel {
@@ -49,4 +55,11 @@
color: #292524;
padding-left: 0.5em;
border-radius: 3px;
+ margin-bottom: 1em;
+ border: 1px solid grey;
+}
+
+.topic-chip.active {
+ background-color: rgb(59 130 246 / 0.15);
+ color: rgb(59 130 246);
}
\ No newline at end of file
diff --git a/static/publications.js b/static/publications.js
index 9056ef8..8bbb1ce 100644
--- a/static/publications.js
+++ b/static/publications.js
@@ -1,13 +1,5 @@
-// Load data
-const rawData = JSON.parse(document.getElementById("pub-data").textContent);
-
-// Normalize data
-const publications = rawData.map(p => ({
- ...p,
- year: p.date ? new Date(p.date).getFullYear() : 'unknown',
- topics: (p.topic || []).map(t => t.display_label),
- authors: (p.author || []).map(a => `${a.given_name} ${a.family_name}`)
-}));
+// Load data from all pre-rendered publication item divs
+const publications = Array.from(document.querySelectorAll(".pub"));
// State
let state = {
@@ -20,15 +12,13 @@ let state = {
// Build filter options dynamically
function getUniqueValues(field) {
const values = new Set();
-
- publications.forEach(p => {
- if (Array.isArray(p[field])) {
- p[field].forEach(v => values.add(v));
- } else if (p[field]) {
- values.add(p[field]);
+ publications.forEach(el => {
+ if (field === "topic") {
+ getTopics(el).forEach(v => values.add(v));
+ } else {
+ values.add(el.dataset[field]);
}
});
-
return Array.from(values).sort();
}
@@ -42,7 +32,6 @@ function formatValue(field, value) {
return value
}
-// Render checkboxes for filters
function renderFilter(containerId, field, values) {
const container = document.getElementById(containerId);
values.forEach(value => {
@@ -52,6 +41,8 @@ function renderFilter(containerId, field, values) {
const checkbox = document.createElement("input");
checkbox.type = "checkbox";
checkbox.value = value;
+ checkbox.dataset.field = field;
+ checkbox.id = id;
checkbox.addEventListener("change", () => {
if (checkbox.checked) {
state[field].add(value);
@@ -67,67 +58,118 @@ function renderFilter(containerId, field, values) {
}
// Filtering logic
-function matchesFilters(p) {
- // Kind
- if (state.kind.size && !state.kind.has(p.kind)) return false;
- // Year
- if (state.year.size && !state.year.has(String(p.year))) return false;
- // Topic (multi-value)
+function matchesFilters(el) {
+ const kind = el.dataset.kind;
+ const year = el.dataset.year;
+ const topics = getTopics(el);
+
+ // kind filter
+ if (state.kind.size && !state.kind.has(kind)) {
+ return false;
+ }
+ // year filter
+ if (state.year.size && !state.year.has(year)) {
+ return false;
+ }
+ // topic filter (multi-match)
if (state.topic.size) {
- const match = p.topics.some(t => state.topic.has(t));
+ const match = topics.some(t => state.topic.has(t));
if (!match) return false;
}
return true;
}
-// Search
-function matchesSearch(p) {
+// Helper to get topics from an element
+function getTopics(el) {
+ try {
+ return JSON.parse(el.dataset.topics || "[]").map(t => t.display_label);
+ } catch {
+ return [];
+ }
+}
+
+// Search logic
+function matchesSearch(el) {
if (!state.search) return true;
const text = (
- p.title +
- " " +
- p.kind +
- " " +
- p.topics.join(" ") +
- " " +
- p.authors.join(" ")
+ el.dataset.title + " " +
+ el.dataset.kind + " " +
+ el.dataset.year + " " +
+ el.dataset.topics + " " +
+ el.dataset.authors
).toLowerCase();
+
return text.includes(state.search);
}
-// Render results
+// Change display of divs based on filtering/searching
function render() {
- const container = document.getElementById("results");
- container.innerHTML = "";
- const filtered = publications.filter(p =>
- matchesFilters(p) && matchesSearch(p)
- );
- if (filtered.length === 0) {
- container.innerHTML = "No results
";
- return;
- }
- filtered.forEach(p => {
- const div = document.createElement("div");
- div.className = "pub";
- div.innerHTML = `
- ${p.title} ${p.year ? ' ('+p.year+')' : ''}
- Authors: ${p.authors.join(", ")}
- Kind: ${formatValue("kind", p.kind)}
- Topics: ${p.topics.join(", ")}
- `;
- container.appendChild(div);
+ let count = 0;
+ publications.forEach(el => {
+ const visible =
+ matchesFilters(el) &&
+ matchesSearch(el);
+ if (visible) count+=1;
+ renderCount(count)
+ el.style.display = visible ? "" : "none";
});
}
+// Count of searched+filtered publications
+function renderCount(count) {
+ const countEl = document.getElementById('pub-count');
+ countEl.innerHTML = `${count}` ;
+}
+
+// Set checkbox if user clicks on topic pill
+function selectFilter(field, value) {
+ const checkbox = document.querySelector(
+ `input[type="checkbox"][data-field="${field}"][value="${CSS.escape(value)}"]`
+ );
+ if (!checkbox) return;
+ if (!checkbox.checked) {
+ checkbox.checked = true;
+ state[field].add(value);
+ render();
+ }
+}
+
+// Clear all
+function clearAllFilters() {
+ // Reset state
+ state.kind.clear();
+ state.topic.clear();
+ state.year.clear();
+ state.search = "";
+ // Uncheck all checkboxes
+ document.querySelectorAll('input[type="checkbox"]').forEach(cb => {
+ cb.checked = false;
+ });
+ // Clear search input
+ const searchInput = document.getElementById("search");
+ if (searchInput) {
+ searchInput.value = "";
+ }
+ // Re-render results
+ render();
+}
+
// On startup:
-// Build filters
+// 1) Build filters
renderFilter("filter-kind", "kind", getUniqueValues("kind"));
-renderFilter("filter-topic", "topic", getUniqueValues("topics"));
+renderFilter("filter-topic", "topic", getUniqueValues("topic"));
renderFilter("filter-year", "year", getUniqueValues("year").map(String));
-// Search input
+// 2) Register search input
document.getElementById("search").addEventListener("input", e => {
state.search = e.target.value.toLowerCase();
render();
});
-// Initial render
+// 3) Add topic click handlers
+document.querySelectorAll(".topic-chip").forEach(el => {
+ el.addEventListener("click", () => {
+ const topic = el.dataset.topic;
+ selectFilter("topic", topic);
+ });
+});
+// 4) Initial render
render();
\ No newline at end of file