import * as React from 'react'; import flatMap from 'array.prototype.flatmap'; import Linkify from 'react-linkify'; import {FormattedMessage, InjectedIntl} from 'react-intl'; import {GedcomData, pointerToId} from './util/gedcom_util'; import {GedcomEntry} from 'parse-gedcom'; import {intlShape} from 'react-intl'; import {translateDate} from './util/date_util'; interface Props { gedcom: GedcomData; indi: string; } const EVENT_TAGS = ['BIRT', 'BAPM', 'CHR', 'DEAT', 'BURI', 'EVEN', 'CENS']; const EXCLUDED_TAGS = ['NAME', 'SEX', 'FAMC', 'FAMS', 'NOTE', 'SOUR']; const TAG_DESCRIPTIONS = new Map([ ['BAPM', 'Baptism'], ['BIRT', 'Birth'], ['BURI', 'Burial'], ['CENS', 'Census'], ['CHR', 'Christening'], ['DEAT', 'Death'], ['EMAIL', 'E-mail'], ['EVEN', 'Event'], ['OCCU', 'Occupation'], ['TITL', 'Title'], ['WWW', 'WWW'], ]); function translateTag(tag: string) { return ( ); } function joinLines(lines: (JSX.Element | string)[]) { return ( <> {lines.map((line, index) => (
{line}
))} ); } /** * Returns the data for the given GEDCOM entry as an array of lines. Supports * continuations with CONT and CONC. */ function getData(entry: GedcomEntry) { const result = [entry.data]; entry.tree.forEach((subentry) => { if (subentry.tag === 'CONC' && subentry.data) { const last = result.length - 1; result[last] += subentry.data; } else if (subentry.tag === 'CONT' && subentry.data) { result.push(subentry.data); } }); return result; } function eventDetails(entry: GedcomEntry, intl: InjectedIntl) { const lines = []; if (entry.data && entry.data.length > 1) { lines.push({entry.data}); } const date = entry.tree.find((subentry) => subentry.tag === 'DATE'); if (date && date.data) { lines.push(translateDate(date.data, intl)); } const place = entry.tree.find((subentry) => subentry.tag === 'PLAC'); if (place && place.data) { lines.push(...getData(place)); } entry.tree .filter((subentry) => subentry.tag === 'NOTE') .forEach((note) => getData(note).forEach((line) => lines.push({line})), ); if (!lines.length) { return null; } return ( <>
{translateTag(entry.tag)}
{joinLines(lines)} ); } function dataDetails(entry: GedcomEntry) { const lines = []; if (entry.data) { lines.push(...getData(entry)); } entry.tree .filter((subentry) => subentry.tag === 'NOTE') .forEach((note) => getData(note).forEach((line) => lines.push({line})), ); if (!lines.length) { return null; } return ( <>
{translateTag(entry.tag)}
{joinLines(lines)} ); } function noteDetails(entry: GedcomEntry) { return joinLines( getData(entry).map((line, index) => {line}), ); } function nameDetails(entry: GedcomEntry) { return (

{entry.data .split('/') .filter((name) => !!name) .map((name, index) => (
{name}
))}

); } function getDetails( entries: GedcomEntry[], tags: string[], detailsFunction: (entry: GedcomEntry) => JSX.Element | null, ): JSX.Element[] { return flatMap(tags, (tag) => entries .filter((entry) => entry.tag === tag) .map((entry) => detailsFunction(entry)), ) .filter((element) => element !== null) .map((element, index) => (
{element}
)); } /** * Returns true if there is displayable information in this entry. * Returns false if there is no data in this entry or this is only a reference * to another entry. */ function hasData(entry: GedcomEntry) { return entry.tree.length > 0 || (entry.data && !entry.data.startsWith('@')); } function getOtherDetails(entries: GedcomEntry[]) { return entries .filter( (entry) => !EXCLUDED_TAGS.includes(entry.tag) && !EVENT_TAGS.includes(entry.tag), ) .filter(hasData) .map((entry) => dataDetails(entry)) .filter((element) => element !== null) .map((element, index) => (
{element}
)); } /** * If the entry is a reference to a top-level entry, the referenced entry is * returned. Otherwise, returns the given entry unmodified. */ function dereference(entry: GedcomEntry, gedcom: GedcomData) { if (entry.data) { const dereferenced = gedcom.other[pointerToId(entry.data)]; if (dereferenced) { return dereferenced; } } return entry; } export class Details extends React.Component { /** Make intl appear in this.context. */ static contextTypes = { intl: intlShape, }; render() { const entries = this.props.gedcom.indis[this.props.indi].tree; const entriesWithData = entries .map((entry) => dereference(entry, this.props.gedcom)) .filter(hasData); return (
{getDetails(entries, ['NAME'], nameDetails)} {getDetails(entries, EVENT_TAGS, (entry) => eventDetails(entry, this.context.intl as InjectedIntl), )} {getOtherDetails(entriesWithData)} {getDetails(entriesWithData, ['NOTE'], noteDetails)}
); } }