diff --git a/.gitignore b/.gitignore
index bb28cfd..3cadc76 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,4 @@ cypress/plugins/index.js
cypress/support/commands.js
cypress/support/index.js
cypress/videos
+.idea
diff --git a/src/app.tsx b/src/app.tsx
index d866c5c..d784049 100644
--- a/src/app.tsx
+++ b/src/app.tsx
@@ -11,7 +11,7 @@ import {
DEFALUT_CONFIG,
} from './config';
import {DataSourceEnum, SourceSelection} from './datasource/data_source';
-import {Details} from './details';
+import {Details} from './details/details';
import {EmbeddedDataSource, EmbeddedSourceSpec} from './datasource/embedded';
import {FormattedMessage, injectIntl, WrappedComponentProps} from 'react-intl';
import {getI18nMessage} from './util/error_i18n';
diff --git a/src/details.tsx b/src/details.tsx
deleted file mode 100644
index d6f5ecb..0000000
--- a/src/details.tsx
+++ /dev/null
@@ -1,235 +0,0 @@
-import * as React from 'react';
-import flatMap from 'array.prototype.flatmap';
-import Linkify from 'react-linkify';
-import {
- FormattedMessage,
- injectIntl,
- IntlShape,
- WrappedComponentProps,
-} from 'react-intl';
-import {GedcomData, pointerToId} from './util/gedcom_util';
-import {GedcomEntry} from 'parse-gedcom';
-import {translateDate} from './util/date_util';
-
-interface Props {
- gedcom: GedcomData;
- indi: string;
-}
-
-const EVENT_TAGS = ['BIRT', 'BAPM', 'CHR', 'EVEN', 'CENS', 'DEAT', 'BURI'];
-const EXCLUDED_TAGS = ['NAME', 'SEX', 'FAMC', 'FAMS', 'NOTE', 'SOUR'];
-const TAG_DESCRIPTIONS = new Map([
- ['ADOP', 'Adoption'],
- ['BAPM', 'Baptism'],
- ['BIRT', 'Birth'],
- ['BURI', 'Burial'],
- ['CENS', 'Census'],
- ['CHR', 'Christening'],
- ['CREM', 'Cremation'],
- ['DEAT', 'Death'],
- ['EDUC', 'Education'],
- ['EMAIL', 'E-mail'],
- ['EMIG', 'Emigration'],
- ['EVEN', 'Event'],
- ['FACT', 'Fact'],
- ['IMMI', 'Immigration'],
- ['MARR', 'Marriage'],
- ['MILT', 'Military services'],
- ['NATU', 'Naturalization'],
- ['OCCU', 'Occupation'],
- ['TITL', 'Title'],
- ['WWW', 'WWW'],
-]);
-
-function translateTag(tag: string) {
- const normalizedTag = tag.replace(/_/g, '');
- 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: IntlShape) {
- 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;
-}
-
-class DetailsComponent extends React.Component<
- Props & WrappedComponentProps,
- {}
-> {
- 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.props.intl),
- )}
- {getOtherDetails(entriesWithData)}
- {getDetails(entriesWithData, ['NOTE'], noteDetails)}
-
- );
- }
-}
-export const Details = injectIntl(DetailsComponent);
diff --git a/src/details/details.tsx b/src/details/details.tsx
new file mode 100644
index 0000000..4c0f5b5
--- /dev/null
+++ b/src/details/details.tsx
@@ -0,0 +1,148 @@
+import * as React from 'react';
+import flatMap from 'array.prototype.flatmap';
+import {injectIntl, WrappedComponentProps} from 'react-intl';
+import {dereference, GedcomData, getData} from '../util/gedcom_util';
+import {GedcomEntry} from 'parse-gedcom';
+import {TranslatedTag} from './translated-tag';
+import {Events} from './events';
+import {MultilineText} from './multiline-text';
+
+interface Props {
+ gedcom: GedcomData;
+ indi: string;
+}
+
+const EXCLUDED_TAGS = [
+ 'BIRT',
+ 'BAPM',
+ 'CHR',
+ 'EVEN',
+ 'CENS',
+ 'DEAT',
+ 'BURI',
+ 'NAME',
+ 'SEX',
+ 'FAMC',
+ 'FAMS',
+ 'NOTE',
+ 'SOUR',
+];
+
+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 (
+ <>
+
+
+
+
+
+
+ >
+ );
+}
+
+function noteDetails(entry: GedcomEntry) {
+ return (
+ (
+ {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))
+ .filter(hasData)
+ .map((entry) => dataDetails(entry))
+ .filter((element) => element !== null)
+ .map((element, index) => (
+
+ {element}
+
+ ));
+}
+
+class DetailsComponent extends React.Component<
+ Props & WrappedComponentProps,
+ {}
+> {
+ render() {
+ const entries = this.props.gedcom.indis[this.props.indi].tree;
+ const entriesWithData = entries
+ .map((entry) =>
+ dereference(entry, this.props.gedcom, (gedcom) => gedcom.other),
+ )
+ .filter(hasData);
+
+ return (
+
+ {getDetails(entries, ['NAME'], nameDetails)}
+
+ {getOtherDetails(entriesWithData)}
+ {getDetails(entriesWithData, ['NOTE'], noteDetails)}
+
+ );
+ }
+}
+export const Details = injectIntl(DetailsComponent);
diff --git a/src/details/events.tsx b/src/details/events.tsx
new file mode 100644
index 0000000..87031e2
--- /dev/null
+++ b/src/details/events.tsx
@@ -0,0 +1,235 @@
+import * as React from 'react';
+import {injectIntl, IntlShape, WrappedComponentProps} from 'react-intl';
+import {dereference, GedcomData, getData} from '../util/gedcom_util';
+import {GedcomEntry} from 'parse-gedcom';
+import {compareDates, translateDate} from '../util/date_util';
+import {DateOrRange, getDate} from 'topola';
+import {TranslatedTag} from './translated-tag';
+import {MultilineText} from './multiline-text';
+import flatMap from 'array.prototype.flatmap';
+
+interface Props {
+ gedcom: GedcomData;
+ indi: string;
+ entries: GedcomEntry[];
+}
+
+interface Event {
+ type: string;
+ date: DateOrRange | undefined;
+ header: JSX.Element;
+ subHeader: JSX.Element | null;
+ place: JSX.Element | null;
+ notes: JSX.Element | null;
+}
+
+const EVENT_TAGS = [
+ 'BIRT',
+ 'BAPM',
+ 'CHR',
+ 'FAMS',
+ 'EVEN',
+ 'CENS',
+ 'DEAT',
+ 'BURI',
+];
+
+const FAMILY_EVENT_TAGS = ['MARR', 'DIV'];
+
+function eventHeader(tag: string, date: GedcomEntry | null, intl: IntlShape) {
+ return (
+
+
+
+
+ {date && date.data ? (
+
+ {translateDate(date.data, intl)}
+
+ ) : null}
+
+ );
+}
+
+function eventFamilyDetails(
+ entry: GedcomEntry,
+ indi: string,
+ familyEntry: GedcomEntry,
+ gedcom: GedcomData,
+) {
+ const spouseReference = familyEntry.tree
+ .filter((familySubEntry) => ['WIFE', 'HUSB'].includes(familySubEntry.tag))
+ .find((familySubEntry) => !familySubEntry.data.includes(indi));
+
+ if (spouseReference) {
+ const spouseName = dereference(
+ spouseReference,
+ gedcom,
+ (gedcom) => gedcom.indis,
+ )
+ .tree.filter((subEntry) => subEntry.tag === 'NAME')
+ .find(
+ (subEntry) =>
+ subEntry.tree.filter(
+ (nameEntry) =>
+ nameEntry.tag === 'TYPE' && nameEntry.data === 'married',
+ ).length === 0,
+ );
+ if (spouseName) {
+ return {spouseName.data.replaceAll('/', '')}
;
+ }
+ }
+ return null;
+}
+
+function eventPlace(entry: GedcomEntry) {
+ const place = entry.tree.find((subEntry) => subEntry.tag === 'PLAC');
+ if (place && place.data) {
+ return {getData(place)}
;
+ }
+ return null;
+}
+
+function eventNotes(entry: GedcomEntry, gedcom: GedcomData) {
+ const notes = entry.tree
+ .filter((subentry) => ['NOTE', 'TYPE'].includes(subentry.tag))
+ .map((note) => dereference(note, gedcom, (gedcom) => gedcom.other))
+ .map((note) => noteDetails(note));
+
+ if (notes && notes.length) {
+ return (
+
+ {notes.map((note, index) => (
+
{note}
+ ))}
+
+ );
+ }
+ return null;
+}
+
+function noteDetails(entry: GedcomEntry) {
+ return (
+ (
+ {line}
+ ))}
+ />
+ );
+}
+
+function eventDetails(event: Event) {
+ return (
+
+ {event.header}
+ {event.subHeader}
+ {event.place}
+ {event.notes}
+
+ );
+}
+
+function getEventDetails(
+ entries: GedcomEntry[],
+ gedcom: GedcomData,
+ indi: string,
+ intl: IntlShape,
+): JSX.Element | null {
+ const events = flatMap(EVENT_TAGS, (tag) =>
+ entries
+ .filter((entry) => entry.tag === tag)
+ .map((eventEntry) => toEvent(eventEntry, gedcom, indi, intl))
+ .flatMap((events) => events)
+ .sort((event1, event2) => compareDates(event1.date, event2.date))
+ .map((event) => eventDetails(event)),
+ );
+ if (events && events.length) {
+ return (
+
+ {events.map((eventElement, index) => (
+
+ {eventElement}
+
+ ))}
+
+ );
+ }
+ return null;
+}
+
+function toEvent(
+ entry: GedcomEntry,
+ gedcom: GedcomData,
+ indi: string,
+ intl: IntlShape,
+): Event[] {
+ if (entry.tag === 'FAMS') {
+ return toFamilyEvents(entry, gedcom, indi, intl);
+ }
+ return toIndiEvent(entry, gedcom, indi, intl);
+}
+
+function toIndiEvent(
+ entry: GedcomEntry,
+ gedcom: GedcomData,
+ indi: string,
+ intl: IntlShape,
+): Event[] {
+ const date = resolveDate(entry) || null;
+ return [
+ {
+ date: date ? getDate(date.data) : undefined,
+ type: entry.tag,
+ header: eventHeader(entry.tag, date, intl),
+ subHeader: null,
+ place: eventPlace(entry),
+ notes: eventNotes(entry, gedcom),
+ },
+ ];
+}
+
+function resolveDate(entry: GedcomEntry) {
+ return entry.tree.find((subEntry) => subEntry.tag === 'DATE');
+}
+
+function toFamilyEvents(
+ entry: GedcomEntry,
+ gedcom: GedcomData,
+ indi: string,
+ intl: IntlShape,
+): Event[] {
+ const family = dereference(entry, gedcom, (gedcom) => gedcom.fams);
+ return flatMap(FAMILY_EVENT_TAGS, (tag) =>
+ family.tree.filter((entry) => entry.tag === tag),
+ ).map((familyMarriageEvent) => {
+ const date = resolveDate(familyMarriageEvent) || null;
+ return {
+ date: date ? getDate(date.data) : undefined,
+ type: familyMarriageEvent.tag,
+ header: eventHeader(familyMarriageEvent.tag, date, intl),
+ subHeader: eventFamilyDetails(familyMarriageEvent, indi, family, gedcom),
+ place: eventPlace(familyMarriageEvent),
+ notes: eventNotes(familyMarriageEvent, gedcom),
+ };
+ });
+}
+
+class EventsComponent extends React.Component<
+ Props & WrappedComponentProps,
+ {}
+> {
+ render() {
+ return (
+ <>
+ {getEventDetails(
+ this.props.entries,
+ this.props.gedcom,
+ this.props.indi,
+ this.props.intl,
+ )}
+ >
+ );
+ }
+}
+
+export const Events = injectIntl(EventsComponent);
diff --git a/src/details/multiline-text.tsx b/src/details/multiline-text.tsx
new file mode 100644
index 0000000..2a5a738
--- /dev/null
+++ b/src/details/multiline-text.tsx
@@ -0,0 +1,31 @@
+import * as React from 'react';
+import {injectIntl, WrappedComponentProps} from 'react-intl';
+import Linkify from 'react-linkify';
+
+interface Props {
+ lines: (JSX.Element | string)[];
+}
+
+function joinLines(lines: (JSX.Element | string)[]) {
+ return (
+ <>
+ {lines.map((line, index) => (
+
+ {line}
+
+
+ ))}
+ >
+ );
+}
+
+class MultilineTextComponent extends React.Component<
+ Props & WrappedComponentProps,
+ {}
+> {
+ render() {
+ return joinLines(this.props.lines);
+ }
+}
+
+export const MultilineText = injectIntl(MultilineTextComponent);
diff --git a/src/details/translated-tag.tsx b/src/details/translated-tag.tsx
new file mode 100644
index 0000000..5050502
--- /dev/null
+++ b/src/details/translated-tag.tsx
@@ -0,0 +1,51 @@
+import {FormattedMessage, injectIntl, WrappedComponentProps} from 'react-intl';
+import * as React from 'react';
+
+interface Props {
+ tag: string;
+}
+
+const TAG_DESCRIPTIONS = new Map([
+ ['ADOP', 'Adoption'],
+ ['BAPM', 'Baptism'],
+ ['BIRT', 'Birth'],
+ ['BURI', 'Burial'],
+ ['CENS', 'Census'],
+ ['CHR', 'Christening'],
+ ['CREM', 'Cremation'],
+ ['DEAT', 'Death'],
+ ['EDUC', 'Education'],
+ ['EMAIL', 'E-mail'],
+ ['EMIG', 'Emigration'],
+ ['EVEN', 'Event'],
+ ['FACT', 'Fact'],
+ ['IMMI', 'Immigration'],
+ ['MARR', 'Marriage'],
+ ['DIV', 'Divorce'],
+ ['MILT', 'Military services'],
+ ['NATU', 'Naturalization'],
+ ['OCCU', 'Occupation'],
+ ['TITL', 'Title'],
+ ['WWW', 'WWW'],
+]);
+
+function translateTag(tag: string) {
+ const normalizedTag = tag.replace(/_/g, '');
+ return (
+
+ );
+}
+
+class TranslatedTagComponent extends React.Component<
+ Props & WrappedComponentProps,
+ {}
+> {
+ render() {
+ return translateTag(this.props.tag);
+ }
+}
+
+export const TranslatedTag = injectIntl(TranslatedTagComponent);
diff --git a/src/translations/pl.json b/src/translations/pl.json
index 07cfe0e..f67a989 100644
--- a/src/translations/pl.json
+++ b/src/translations/pl.json
@@ -54,6 +54,8 @@
"gedcom.TITL": "Tytuł",
"gedcom.WWW": "Strona WWW",
"gedcom._UPD": "Ostatnia aktualizacja",
+ "gedcom.MARR": "Małżeństwo",
+ "gedcom.DIV": "Rozwód",
"date.abt": "około",
"date.cal": "wyliczone",
"date.est": "oszacowane",
diff --git a/src/util/date_util.ts b/src/util/date_util.ts
index fed7a5d..031eee9 100644
--- a/src/util/date_util.ts
+++ b/src/util/date_util.ts
@@ -97,3 +97,34 @@ export function formatDateOrRange(
export function translateDate(gedcomDate: string, intl: IntlShape): string {
return formatDateOrRange(getDate(gedcomDate), intl);
}
+
+/** Compares a dates given in GEDCOM format. */
+export function compareDates(
+ firstDateOrRange: DateOrRange | undefined,
+ secondDateOrRange: DateOrRange | undefined,
+): number {
+ const date1 =
+ firstDateOrRange &&
+ (firstDateOrRange.date ||
+ (firstDateOrRange.dateRange && firstDateOrRange.dateRange.from));
+ const date2 =
+ secondDateOrRange &&
+ (secondDateOrRange.date ||
+ (secondDateOrRange.dateRange && secondDateOrRange.dateRange.from));
+ if (!date1 || !date1.year || !date2 || !date2.year) {
+ return 0;
+ }
+ if (date1.year !== date2.year) {
+ return date1.year - date2.year;
+ }
+ if (!date1.month || !date2.month) {
+ return 0;
+ }
+ if (date1.month !== date2.month) {
+ return date1.month - date2.month;
+ }
+ if (date1.day && date2.day && date1.day !== date2.day) {
+ return date1.month - date2.month;
+ }
+ return 0;
+}
diff --git a/src/util/gedcom_util.ts b/src/util/gedcom_util.ts
index 0381466..887687a 100644
--- a/src/util/gedcom_util.ts
+++ b/src/util/gedcom_util.ts
@@ -1,13 +1,13 @@
import {GedcomEntry, parse as parseGedcom} from 'parse-gedcom';
import {TopolaError} from './error';
import {
+ gedcomEntriesToJson,
JsonFam,
JsonGedcomData,
- JsonIndi,
- gedcomEntriesToJson,
JsonImage,
- JsonEvent,
+ JsonIndi,
} from 'topola';
+import {compareDates} from './date_util';
export interface GedcomData {
/** The HEAD entry. */
@@ -76,33 +76,6 @@ function strcmp(a: string, b: string) {
return 0;
}
-/** Compares dates of the given events. */
-function compareDates(
- event1: JsonEvent | undefined,
- event2: JsonEvent | undefined,
-): number {
- const date1 =
- event1 && (event1.date || (event1.dateRange && event1.dateRange.from));
- const date2 =
- event2 && (event2.date || (event2.dateRange && event2.dateRange.from));
- if (!date1 || !date1.year || !date2 || !date2.year) {
- return 0;
- }
- if (date1.year !== date2.year) {
- return date1.year - date2.year;
- }
- if (!date1.month || !date2.month) {
- return 0;
- }
- if (date1.month !== date2.month) {
- return date1.month - date2.month;
- }
- if (date1.day && date2.day && date1.day !== date2.day) {
- return date1.month - date2.month;
- }
- return 0;
-}
-
/** Birth date comparator for individuals. */
function birthDatesComparator(gedcom: JsonGedcomData) {
const indiMap = idToIndiMap(gedcom);
@@ -179,6 +152,41 @@ function sortSpouses(gedcom: JsonGedcomData): JsonGedcomData {
return Object.assign({}, gedcom, {indis: newIndis});
}
+/**
+ * If the entry is a reference to a top-level entry, the referenced entry is
+ * returned. Otherwise, returns the given entry unmodified.
+ */
+export function dereference(
+ entry: GedcomEntry,
+ gedcom: GedcomData,
+ getterFunction: (gedcom: GedcomData) => {[key: string]: GedcomEntry},
+) {
+ if (entry.data) {
+ const dereferenced = getterFunction(gedcom)[pointerToId(entry.data)];
+ if (dereferenced) {
+ return dereferenced;
+ }
+ }
+ return entry;
+}
+
+/**
+ * Returns the data for the given GEDCOM entry as an array of lines. Supports
+ * continuations with CONT and CONC.
+ */
+export 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;
+}
+
/** Sorts children and spouses. */
export function normalizeGedcom(gedcom: JsonGedcomData): JsonGedcomData {
return sortSpouses(sortChildren(gedcom));
diff --git a/src/util/media.ts b/src/util/media.ts
index 014090e..d5be693 100644
--- a/src/util/media.ts
+++ b/src/util/media.ts
@@ -1,6 +1,6 @@
import {createMedia} from '@artsy/fresnel';
-/** Defines the breakpoints at which to show different UI variants. */
+/** Defines the breakpoints at which to show different UI variants.*/
const AppMedia = createMedia({
breakpoints: {
small: 320,