mirror of
https://github.com/PeWu/topola-viewer.git
synced 2026-04-21 05:56:17 +00:00
Refactor details panel to use React Semantic Components (#98)
This commit is contained in:
124
src/config.tsx
124
src/config.tsx
@@ -1,4 +1,4 @@
|
|||||||
import {Checkbox, Form} from 'semantic-ui-react';
|
import {Item, Checkbox, Form, Header} from 'semantic-ui-react';
|
||||||
import {FormattedMessage} from 'react-intl';
|
import {FormattedMessage} from 'react-intl';
|
||||||
import {ParsedQuery} from 'query-string';
|
import {ParsedQuery} from 'query-string';
|
||||||
|
|
||||||
@@ -44,64 +44,68 @@ export function ConfigPanel(props: {
|
|||||||
onChange: (config: Config) => void;
|
onChange: (config: Config) => void;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<>
|
<Form className="details">
|
||||||
<Form className="ui segments details">
|
<Item.Group>
|
||||||
<div className="ui segment">
|
<Item>
|
||||||
<div className="ui sub header">
|
<Item.Content>
|
||||||
<FormattedMessage id="config.colors" defaultMessage="Colors" />
|
<Header sub>
|
||||||
</div>
|
<FormattedMessage id="config.colors" defaultMessage="Colors" />
|
||||||
<Form.Field className="no-margin">
|
</Header>
|
||||||
<Checkbox
|
<Form.Field className="no-margin">
|
||||||
radio
|
<Checkbox
|
||||||
label={
|
radio
|
||||||
<FormattedMessage
|
label={
|
||||||
tagName="label"
|
<FormattedMessage
|
||||||
id="config.colors.NO_COLOR"
|
tagName="label"
|
||||||
defaultMessage="none"
|
id="config.colors.NO_COLOR"
|
||||||
/>
|
defaultMessage="none"
|
||||||
}
|
/>
|
||||||
name="checkboxRadioGroup"
|
}
|
||||||
value="none"
|
name="checkboxRadioGroup"
|
||||||
checked={props.config.color === ChartColors.NO_COLOR}
|
value="none"
|
||||||
onClick={() => props.onChange({color: ChartColors.NO_COLOR})}
|
checked={props.config.color === ChartColors.NO_COLOR}
|
||||||
/>
|
onClick={() => props.onChange({color: ChartColors.NO_COLOR})}
|
||||||
</Form.Field>
|
/>
|
||||||
<Form.Field className="no-margin">
|
</Form.Field>
|
||||||
<Checkbox
|
<Form.Field className="no-margin">
|
||||||
radio
|
<Checkbox
|
||||||
label={
|
radio
|
||||||
<FormattedMessage
|
label={
|
||||||
tagName="label"
|
<FormattedMessage
|
||||||
id="config.colors.COLOR_BY_GENERATION"
|
tagName="label"
|
||||||
defaultMessage="by generation"
|
id="config.colors.COLOR_BY_GENERATION"
|
||||||
/>
|
defaultMessage="by generation"
|
||||||
}
|
/>
|
||||||
name="checkboxRadioGroup"
|
}
|
||||||
value="generation"
|
name="checkboxRadioGroup"
|
||||||
checked={props.config.color === ChartColors.COLOR_BY_GENERATION}
|
value="generation"
|
||||||
onClick={() =>
|
checked={props.config.color === ChartColors.COLOR_BY_GENERATION}
|
||||||
props.onChange({color: ChartColors.COLOR_BY_GENERATION})
|
onClick={() =>
|
||||||
}
|
props.onChange({color: ChartColors.COLOR_BY_GENERATION})
|
||||||
/>
|
}
|
||||||
</Form.Field>
|
/>
|
||||||
<Form.Field className="no-margin">
|
</Form.Field>
|
||||||
<Checkbox
|
<Form.Field className="no-margin">
|
||||||
radio
|
<Checkbox
|
||||||
label={
|
radio
|
||||||
<FormattedMessage
|
label={
|
||||||
tagName="label"
|
<FormattedMessage
|
||||||
id="config.colors.COLOR_BY_SEX"
|
tagName="label"
|
||||||
defaultMessage="by sex"
|
id="config.colors.COLOR_BY_SEX"
|
||||||
/>
|
defaultMessage="by sex"
|
||||||
}
|
/>
|
||||||
name="checkboxRadioGroup"
|
}
|
||||||
value="gender"
|
name="checkboxRadioGroup"
|
||||||
checked={props.config.color === ChartColors.COLOR_BY_SEX}
|
value="gender"
|
||||||
onClick={() => props.onChange({color: ChartColors.COLOR_BY_SEX})}
|
checked={props.config.color === ChartColors.COLOR_BY_SEX}
|
||||||
/>
|
onClick={() =>
|
||||||
</Form.Field>
|
props.onChange({color: ChartColors.COLOR_BY_SEX})
|
||||||
</div>
|
}
|
||||||
</Form>
|
/>
|
||||||
</>
|
</Form.Field>
|
||||||
|
</Item.Content>
|
||||||
|
</Item>
|
||||||
|
</Item.Group>
|
||||||
|
</Form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {Events} from './events';
|
|||||||
import {GedcomEntry} from 'parse-gedcom';
|
import {GedcomEntry} from 'parse-gedcom';
|
||||||
import {MultilineText} from './multiline-text';
|
import {MultilineText} from './multiline-text';
|
||||||
import {TranslatedTag} from './translated-tag';
|
import {TranslatedTag} from './translated-tag';
|
||||||
|
import {Header, Item} from 'semantic-ui-react';
|
||||||
|
|
||||||
const EXCLUDED_TAGS = [
|
const EXCLUDED_TAGS = [
|
||||||
'BIRT',
|
'BIRT',
|
||||||
@@ -36,9 +37,9 @@ function dataDetails(entry: GedcomEntry) {
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="ui sub header">
|
<Header sub>
|
||||||
<TranslatedTag tag={entry.tag} />
|
<TranslatedTag tag={entry.tag} />
|
||||||
</div>
|
</Header>
|
||||||
<span>
|
<span>
|
||||||
<MultilineText lines={lines} />
|
<MultilineText lines={lines} />
|
||||||
</span>
|
</span>
|
||||||
@@ -58,7 +59,7 @@ function noteDetails(entry: GedcomEntry) {
|
|||||||
|
|
||||||
function nameDetails(entry: GedcomEntry) {
|
function nameDetails(entry: GedcomEntry) {
|
||||||
return (
|
return (
|
||||||
<h2 className="ui header">
|
<Header size="large">
|
||||||
{entry.data
|
{entry.data
|
||||||
.split('/')
|
.split('/')
|
||||||
.filter((name) => !!name)
|
.filter((name) => !!name)
|
||||||
@@ -68,7 +69,7 @@ function nameDetails(entry: GedcomEntry) {
|
|||||||
<br />
|
<br />
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</h2>
|
</Header>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,9 +85,9 @@ function getDetails(
|
|||||||
)
|
)
|
||||||
.filter((element) => element !== null)
|
.filter((element) => element !== null)
|
||||||
.map((element, index) => (
|
.map((element, index) => (
|
||||||
<div className="ui segment" key={index}>
|
<Item key={index}>
|
||||||
{element}
|
<Item.Content>{element}</Item.Content>
|
||||||
</div>
|
</Item>
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,9 +107,9 @@ function getOtherDetails(entries: GedcomEntry[]) {
|
|||||||
.map((entry) => dataDetails(entry))
|
.map((entry) => dataDetails(entry))
|
||||||
.filter((element) => element !== null)
|
.filter((element) => element !== null)
|
||||||
.map((element, index) => (
|
.map((element, index) => (
|
||||||
<div className="ui segment" key={index}>
|
<Item key={index}>
|
||||||
{element}
|
<Item.Content>{element}</Item.Content>
|
||||||
</div>
|
</Item>
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,11 +125,13 @@ export function Details(props: Props) {
|
|||||||
.filter(hasData);
|
.filter(hasData);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="ui segments details">
|
<div className="details">
|
||||||
{getDetails(entries, ['NAME'], nameDetails)}
|
<Item.Group divided>
|
||||||
<Events gedcom={props.gedcom} entries={entries} indi={props.indi} />
|
{getDetails(entries, ['NAME'], nameDetails)}
|
||||||
{getOtherDetails(entriesWithData)}
|
<Events gedcom={props.gedcom} entries={entries} indi={props.indi} />
|
||||||
{getDetails(entriesWithData, ['NOTE'], noteDetails)}
|
{getOtherDetails(entriesWithData)}
|
||||||
|
{getDetails(entriesWithData, ['NOTE'], noteDetails)}
|
||||||
|
</Item.Group>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {Link, useLocation} from 'react-router-dom';
|
|||||||
import {MultilineText} from './multiline-text';
|
import {MultilineText} from './multiline-text';
|
||||||
import {pointerToId} from '../util/gedcom_util';
|
import {pointerToId} from '../util/gedcom_util';
|
||||||
import {TranslatedTag} from './translated-tag';
|
import {TranslatedTag} from './translated-tag';
|
||||||
|
import {Header, Item} from 'semantic-ui-react';
|
||||||
|
|
||||||
function PersonLink(props: {person: GedcomEntry}) {
|
function PersonLink(props: {person: GedcomEntry}) {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
@@ -23,11 +24,11 @@ function PersonLink(props: {person: GedcomEntry}) {
|
|||||||
search['indi'] = pointerToId(props.person.pointer);
|
search['indi'] = pointerToId(props.person.pointer);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="meta">
|
<Item.Meta>
|
||||||
<Link to={{pathname: '/view', search: queryString.stringify(search)}}>
|
<Link to={{pathname: '/view', search: queryString.stringify(search)}}>
|
||||||
{name}
|
{name}
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</Item.Meta>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,14 +63,14 @@ const FAMILY_EVENT_TAGS = ['MARR', 'DIV'];
|
|||||||
function EventHeader(props: {event: EventData}) {
|
function EventHeader(props: {event: EventData}) {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="event-header">
|
||||||
<span style={{textTransform: 'uppercase'}} className="ui small header">
|
<Header as="span" size="small">
|
||||||
<TranslatedTag tag={props.event.type} />
|
<TranslatedTag tag={props.event.type} />
|
||||||
</span>
|
</Header>
|
||||||
{props.event.date ? (
|
{props.event.date ? (
|
||||||
<span className="ui sub header right floated">
|
<Header as="span" textAlign="right" sub>
|
||||||
{formatDateOrRange(props.event.date, intl)}
|
{formatDateOrRange(props.event.date, intl)}
|
||||||
</span>
|
</Header>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -176,18 +177,18 @@ function toFamilyEvents(
|
|||||||
|
|
||||||
function Event(props: {event: EventData}) {
|
function Event(props: {event: EventData}) {
|
||||||
return (
|
return (
|
||||||
<div className="ui attached item">
|
<Item>
|
||||||
<div className="content">
|
<Item.Content>
|
||||||
<EventHeader event={props.event} />
|
<EventHeader event={props.event} />
|
||||||
{!!props.event.age && <div className="meta">{props.event.age}</div>}
|
{!!props.event.age && <Item.Meta>{props.event.age}</Item.Meta>}
|
||||||
{!!props.event.personLink && (
|
{!!props.event.personLink && (
|
||||||
<PersonLink person={props.event.personLink} />
|
<PersonLink person={props.event.personLink} />
|
||||||
)}
|
)}
|
||||||
{!!props.event.place && (
|
{!!props.event.place && (
|
||||||
<div className="description">{props.event.place}</div>
|
<Item.Description>{props.event.place}</Item.Description>
|
||||||
)}
|
)}
|
||||||
{!!props.event.notes.length && (
|
{!!props.event.notes.length && (
|
||||||
<div className="description">
|
<Item.Description>
|
||||||
{props.event.notes.map((note, index) => (
|
{props.event.notes.map((note, index) => (
|
||||||
<div key={index}>
|
<div key={index}>
|
||||||
<MultilineText
|
<MultilineText
|
||||||
@@ -197,10 +198,10 @@ function Event(props: {event: EventData}) {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</Item.Description>
|
||||||
)}
|
)}
|
||||||
</div>
|
</Item.Content>
|
||||||
</div>
|
</Item>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,11 +217,11 @@ export function Events(props: Props) {
|
|||||||
);
|
);
|
||||||
if (events.length) {
|
if (events.length) {
|
||||||
return (
|
return (
|
||||||
<div className="ui segment divided items">
|
<>
|
||||||
{events.map((event, index) => (
|
{events.map((event, index) => (
|
||||||
<Event event={event} key={index} />
|
<Event event={event} key={index} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -143,9 +143,25 @@ div.zoom {
|
|||||||
margin-top: 0px;
|
margin-top: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui.segments.details {
|
.details {
|
||||||
margin: 0px !important;
|
padding: 15px 0px;
|
||||||
border: 0px !important;
|
border-bottom: 1px solid rgba(34,36,38,.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.details .ui.items .item .content {
|
||||||
|
padding: 0 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details .event-header {
|
||||||
|
justify-content: space-between;
|
||||||
|
display: flex;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details .event-header .header {
|
||||||
|
text-transform: uppercase;
|
||||||
|
margin: 0;
|
||||||
|
min-width: 40%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui.form .field.no-margin {
|
.ui.form .field.no-margin {
|
||||||
|
|||||||
Reference in New Issue
Block a user