From 7c5203b2638b103b2ccb84b190feecb441a2ac21 Mon Sep 17 00:00:00 2001 From: Frank Buchholz Date: Fri, 13 Feb 2026 23:55:23 +0100 Subject: [PATCH] Show header information of the gedcom file on the side panel (#252) * Show header information of the gedcom file on the side panel #251 * Format creation date in HEAD section --- src/index.css | 4 ++ src/sidepanel/config/config.tsx | 7 ++- src/sidepanel/head/head.tsx | 108 ++++++++++++++++++++++++++++++++ src/sidepanel/side-panel.tsx | 2 +- src/translations/bg.json | 1 + src/translations/cs.json | 1 + src/translations/de.json | 1 + src/translations/fr.json | 1 + src/translations/it.json | 1 + src/translations/pl.json | 3 +- src/translations/ru.json | 1 + 11 files changed, 127 insertions(+), 3 deletions(-) create mode 100644 src/sidepanel/head/head.tsx diff --git a/src/index.css b/src/index.css index 04760f1..6e2a544 100644 --- a/src/index.css +++ b/src/index.css @@ -178,6 +178,10 @@ div.zoom { text-transform: uppercase; } +.ui.sub.header, .ui.list { + margin-left: 20px; +} + .limit-height { height: 300px; overflow-y: scroll; diff --git a/src/sidepanel/config/config.tsx b/src/sidepanel/config/config.tsx index d131116..6a26864 100644 --- a/src/sidepanel/config/config.tsx +++ b/src/sidepanel/config/config.tsx @@ -1,3 +1,5 @@ +import {SourceHead} from '../head/head'; +import {GedcomData} from '../../util/gedcom_util'; import {ParsedQuery} from 'query-string'; import {FormattedMessage} from 'react-intl'; import {Checkbox, Form, Header, Item} from 'semantic-ui-react'; @@ -74,11 +76,13 @@ export function configToArgs(config: Config): ParsedQuery { } export function ConfigPanel(props: { + gedcom: GedcomData; config: Config; onChange: (config: Config) => void; }) { return ( -
+ <> + {SourceHead(props.gedcom)} @@ -230,5 +234,6 @@ export function ConfigPanel(props: {
+ ); } diff --git a/src/sidepanel/head/head.tsx b/src/sidepanel/head/head.tsx new file mode 100644 index 0000000..d7d467f --- /dev/null +++ b/src/sidepanel/head/head.tsx @@ -0,0 +1,108 @@ +import { + dereference, + GedcomData, +} from '../../util/gedcom_util'; +import {FormattedMessage, useIntl} from 'react-intl'; +import {Header, Divider, List} from 'semantic-ui-react'; +import {getDate} from 'topola'; +import {formatDateOrRange} from '../../util/date_util'; + +export function SourceHead(gedcom: GedcomData){ + const head = gedcom.head; + /* Don't show the section if there is no relevant information */ + if (!head || !head.tree) { + return null; + } + + const sour = head.tree.find((entry) => entry.tag === 'SOUR'); + const sour_name = sour && sour.tree && sour.tree.find((entry) => entry.tag === 'NAME')?.data; // Software name + + const date = head.tree.find((entry) => entry.tag === 'DATE'); // Creation date + const intl = useIntl(); + const dateFormatted = date ? formatDateOrRange(getDate(date.data), intl) : null; // Formatted creation date + + const file = head.tree.find((entry) => entry.tag === 'FILE')?.data; // File path + const filename = file && ( file.split('\\').pop() || file.split('/').pop() ); // Extract file name from path + const copr = head.tree.find((entry) => entry.tag === 'COPR')?.data; // Copyright + + // Reference to submitter + const submReference = head.tree.find((entry) => entry.tag === 'SUBM'); + const subm = submReference && dereference(submReference, gedcom, (gedcom) => gedcom.other); + + const name = subm && subm.tree && subm.tree.find((entry) => entry.tag === 'NAME')?.data; // Submitter name + const phon = subm && subm.tree && subm.tree.find((entry) => entry.tag === 'PHON')?.data; // Phone number + const email = subm && subm.tree && subm.tree.find((entry) => entry.tag === 'EMAIL')?.data; // Email + + const addr = subm && subm.tree && subm.tree.find((entry) => entry.tag === 'ADDR'); // Address + const adr1 = addr && addr.tree && addr.tree.find((entry) => entry.tag === 'ADR1')?.data; // Street address + const city = addr && addr.tree && addr.tree.find((entry) => entry.tag === 'CITY')?.data; // City + const post = addr && addr.tree && addr.tree.find((entry) => entry.tag === 'POST')?.data; // Postal code + const location = [adr1, post, city].filter(Boolean).join(', '); // Combined location + + /* Don't show the section if there is no relevant information */ + if (!(sour_name || dateFormatted || filename || copr || name || phon || email || location)) { + return null; + } + + // Icons: https://react.semantic-ui.com/elements/icon/ + return ( + <> +
+ +
+ + {sour_name && ( + + + {sour_name} + + )} + + {date && ( + + + {dateFormatted} + + )} + {file && ( + + + {filename} + + )} + {name && ( + + + {name} + + )} + {adr1 && ( + + + {location} + + )} + {phon && ( + + + {phon} + + )} + {email && ( + + + {email} + + )} + {copr && ( + + + {copr} + + )} + + + + + ); +} \ No newline at end of file diff --git a/src/sidepanel/side-panel.tsx b/src/sidepanel/side-panel.tsx index 28a96e9..d54834b 100644 --- a/src/sidepanel/side-panel.tsx +++ b/src/sidepanel/side-panel.tsx @@ -37,7 +37,7 @@ export function SidePanel({ id: 'tab.settings', defaultMessage: 'Settings', }), - render: () => , + render: () => , }, ]; diff --git a/src/translations/bg.json b/src/translations/bg.json index 0bf2dc4..683a6c0 100644 --- a/src/translations/bg.json +++ b/src/translations/bg.json @@ -129,6 +129,7 @@ "config.sex": "Пол", "config.sex.HIDE": "Скриване", "config.sex.SHOW": "Показване", + "head.source": "Източник на данни", "name.unknown_name": "Неизвестно име", "extras.images": "Изображение", "extras.notes": "Бележки", diff --git a/src/translations/cs.json b/src/translations/cs.json index e0a8e44..acdd258 100644 --- a/src/translations/cs.json +++ b/src/translations/cs.json @@ -129,6 +129,7 @@ "config.sex": "Pohlaví", "config.sex.HIDE": "skrýt", "config.sex.SHOW": "zobrazit", + "head.source": "Zdroj dat", "name.unknown_name": "N.N.", "extras.images": "Obrázky", "extras.notes": "Poznámky", diff --git a/src/translations/de.json b/src/translations/de.json index 2b34dc2..ad53fdb 100644 --- a/src/translations/de.json +++ b/src/translations/de.json @@ -129,6 +129,7 @@ "config.sex": "Geschlecht", "config.sex.HIDE": "verbergen", "config.sex.SHOW": "anzeigen", + "head.source": "Datenquelle", "name.unknown_name": "N.N.", "extras.images": "Bilder", "extras.notes": "Notizen", diff --git a/src/translations/fr.json b/src/translations/fr.json index f4ea516..1af1f57 100644 --- a/src/translations/fr.json +++ b/src/translations/fr.json @@ -129,6 +129,7 @@ "config.sex": "Sexe", "config.sex.HIDE": "cacher", "config.sex.SHOW": "afficher", + "head.source": "Source de données", "name.unknown_name": "?", "extras.images": "Images", "extras.notes": "Notes", diff --git a/src/translations/it.json b/src/translations/it.json index 56717f4..59cbd6f 100644 --- a/src/translations/it.json +++ b/src/translations/it.json @@ -129,6 +129,7 @@ "config.sex": "Sesso", "config.sex.HIDE": "nascondere", "config.sex.SHOW": "visualizzare", + "head.source": "Origine dati", "name.unknown_name": "N.N.", "extras.images": "Immagini", "extras.notes": "Appunti", diff --git a/src/translations/pl.json b/src/translations/pl.json index e29798d..f6a266b 100644 --- a/src/translations/pl.json +++ b/src/translations/pl.json @@ -128,7 +128,8 @@ "config.ids.SHOW": "pokaż", "config.sex": "Płeć", "config.sex.HIDE": "ukryj", - "config.sex.SHOW": "pokaż", + "config.sex.SHOW": "pokaż", + "head.source": "Źródło danych", "name.unknown_name": "N.N.", "extras.images": "Zdjęcia", "extras.notes": "Notatki", diff --git a/src/translations/ru.json b/src/translations/ru.json index e6bffaf..23b6662 100644 --- a/src/translations/ru.json +++ b/src/translations/ru.json @@ -129,6 +129,7 @@ "config.sex": "Пол", "config.sex.HIDE": "Скрыть", "config.sex.SHOW": "Показать", + "head.source": "Источник данных", "name.unknown_name": "Неизвестно", "extras.images": "Картинки", "extras.notes": "Примечание",