mirror of
https://github.com/PeWu/topola-viewer.git
synced 2026-02-18 02:55:48 +00:00
Added menu option to display the relatives chart
This commit is contained in:
parent
d091d3e3ce
commit
9bd1720122
9
package-lock.json
generated
9
package-lock.json
generated
@ -8060,8 +8060,7 @@
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.2.4",
|
||||
@ -17998,9 +17997,9 @@
|
||||
}
|
||||
},
|
||||
"topola": {
|
||||
"version": "2.2.9",
|
||||
"resolved": "https://registry.npmjs.org/topola/-/topola-2.2.9.tgz",
|
||||
"integrity": "sha512-bMS3RxmB4P9JmwaiycnbgguIRaXUUAnASyUqGc1GNmloRUBM9yZgGWQMZ4xkFHE7Otr4IiyMp8wOwrXahYLaYQ==",
|
||||
"version": "2.3.4",
|
||||
"resolved": "https://registry.npmjs.org/topola/-/topola-2.3.4.tgz",
|
||||
"integrity": "sha512-eJpy19T6xFkrLgf1NGAMkgmLoswB00hY206LQZWiQRURr0+p0fdGAmNQKkWLba/e/MT3H/Qe/SSnAvRmHhFobA==",
|
||||
"requires": {
|
||||
"d3": "^5.4.0",
|
||||
"d3-flextree": "^2.1.1",
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
"react-router-dom": "^4.3.1",
|
||||
"semantic-ui-css": "^2.4.1",
|
||||
"semantic-ui-react": "^0.84.0",
|
||||
"topola": "^2.2.9"
|
||||
"topola": "^2.3.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/array.prototype.flatmap": "^1.2.0",
|
||||
|
||||
38
src/app.tsx
38
src/app.tsx
@ -1,7 +1,7 @@
|
||||
import * as queryString from 'query-string';
|
||||
import * as React from 'react';
|
||||
import {analyticsEvent} from './analytics';
|
||||
import {Chart} from './chart';
|
||||
import {Chart, ChartType} from './chart';
|
||||
import {Details} from './details';
|
||||
import {getSelection, loadFromUrl, loadGedcom} from './load_data';
|
||||
import {IndiInfo} from 'topola';
|
||||
@ -53,20 +53,27 @@ interface State {
|
||||
hash?: string;
|
||||
/** Error to display. */
|
||||
error?: string;
|
||||
/** True if currently loading. */
|
||||
/** True if data is currently being loaded. */
|
||||
loading: boolean;
|
||||
/** URL of the data that is loaded or is being loaded. */
|
||||
url?: string;
|
||||
/** Whether the side panel is shoen. */
|
||||
/** Whether the side panel is shown. */
|
||||
showSidePanel?: boolean;
|
||||
/** Whether the app is in embedded mode, i.e. embedded in an iframe. */
|
||||
embedded: boolean;
|
||||
/** Whether the app is in standalone mode, i.e. showing 'open file' menus */
|
||||
/** Whether the app is in standalone mode, i.e. showing 'open file' menus. */
|
||||
standalone: boolean;
|
||||
/** Type of displayed chart. */
|
||||
chartType: ChartType;
|
||||
}
|
||||
|
||||
export class App extends React.Component<RouteComponentProps, {}> {
|
||||
state: State = {loading: false, embedded: false, standalone: true};
|
||||
state: State = {
|
||||
loading: false,
|
||||
embedded: false,
|
||||
standalone: true,
|
||||
chartType: ChartType.Hourglass,
|
||||
};
|
||||
chartRef: Chart | null = null;
|
||||
|
||||
private isNewData(
|
||||
@ -81,16 +88,18 @@ export class App extends React.Component<RouteComponentProps, {}> {
|
||||
);
|
||||
}
|
||||
|
||||
/** Sets the state with a new individual selection. */
|
||||
private updateSelection(selection: IndiInfo) {
|
||||
/** Sets the state with a new individual selection and chart type. */
|
||||
private updateDisplay(selection: IndiInfo, chartType?: ChartType) {
|
||||
if (
|
||||
!this.state.selection ||
|
||||
this.state.selection.id !== selection.id ||
|
||||
this.state.selection!.generation !== selection.generation
|
||||
this.state.selection!.generation !== selection.generation ||
|
||||
(chartType !== undefined && chartType !== this.state.chartType)
|
||||
) {
|
||||
this.setState(
|
||||
Object.assign({}, this.state, {
|
||||
selection,
|
||||
chartType: chartType !== undefined ? chartType : this.state.chartType,
|
||||
}),
|
||||
);
|
||||
}
|
||||
@ -179,6 +188,10 @@ export class App extends React.Component<RouteComponentProps, {}> {
|
||||
const hash = getParam('file');
|
||||
const handleCors = getParam('handleCors') !== 'false'; // True by default.
|
||||
const standalone = getParam('standalone') !== 'false'; // True by default.
|
||||
const view = getParam('view');
|
||||
// Hourglass is the default view.
|
||||
const chartType =
|
||||
view === 'relatives' ? ChartType.Relatives : ChartType.Hourglass;
|
||||
|
||||
const gedcom = this.props.location.state && this.props.location.state.data;
|
||||
const images =
|
||||
@ -198,6 +211,7 @@ export class App extends React.Component<RouteComponentProps, {}> {
|
||||
loading: true,
|
||||
url,
|
||||
standalone,
|
||||
chartType,
|
||||
}),
|
||||
);
|
||||
const data = hash
|
||||
@ -221,6 +235,7 @@ export class App extends React.Component<RouteComponentProps, {}> {
|
||||
url,
|
||||
showSidePanel,
|
||||
standalone,
|
||||
chartType,
|
||||
}),
|
||||
);
|
||||
} catch (error) {
|
||||
@ -234,7 +249,7 @@ export class App extends React.Component<RouteComponentProps, {}> {
|
||||
indi,
|
||||
generation,
|
||||
);
|
||||
this.updateSelection(selection);
|
||||
this.updateDisplay(selection, chartType);
|
||||
}
|
||||
}
|
||||
|
||||
@ -246,7 +261,7 @@ export class App extends React.Component<RouteComponentProps, {}> {
|
||||
analyticsEvent('selection_changed');
|
||||
if (this.state.embedded) {
|
||||
// In embedded mode the URL doesn't change.
|
||||
this.updateSelection(selection);
|
||||
this.updateDisplay(selection);
|
||||
return;
|
||||
}
|
||||
const location = this.props.location;
|
||||
@ -263,8 +278,9 @@ export class App extends React.Component<RouteComponentProps, {}> {
|
||||
<div id="content">
|
||||
<Chart
|
||||
data={this.state.data.chartData}
|
||||
onSelection={this.onSelection}
|
||||
selection={this.state.selection}
|
||||
chartType={this.state.chartType}
|
||||
onSelection={this.onSelection}
|
||||
ref={(ref) => (this.chartRef = ref)}
|
||||
/>
|
||||
{this.state.showSidePanel ? (
|
||||
|
||||
@ -10,6 +10,7 @@ import {
|
||||
createChart,
|
||||
DetailedRenderer,
|
||||
HourglassChart,
|
||||
RelativesChart,
|
||||
} from 'topola';
|
||||
|
||||
/** Called when the view is dragged with the mouse. */
|
||||
@ -101,9 +102,16 @@ function canvasToBlob(canvas: HTMLCanvasElement, type: string) {
|
||||
});
|
||||
}
|
||||
|
||||
/** Supported chart types. */
|
||||
export enum ChartType {
|
||||
Hourglass,
|
||||
Relatives,
|
||||
}
|
||||
|
||||
export interface ChartProps {
|
||||
data: JsonGedcomData;
|
||||
selection: IndiInfo;
|
||||
chartType: ChartType;
|
||||
onSelection: (indiInfo: IndiInfo) => void;
|
||||
}
|
||||
|
||||
@ -111,6 +119,18 @@ export interface ChartProps {
|
||||
export class Chart extends React.PureComponent<ChartProps, {}> {
|
||||
private chart?: ChartHandle;
|
||||
|
||||
private getChartType() {
|
||||
switch (this.props.chartType) {
|
||||
case ChartType.Hourglass:
|
||||
return HourglassChart;
|
||||
case ChartType.Relatives:
|
||||
return RelativesChart;
|
||||
default:
|
||||
// Fall back to hourglass chart.
|
||||
return HourglassChart;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the chart or performs a transition animation to a new state.
|
||||
* If indiInfo is not given, it means that it is the initial render and no
|
||||
@ -121,7 +141,7 @@ export class Chart extends React.PureComponent<ChartProps, {}> {
|
||||
(d3.select('#chart').node() as HTMLElement).innerHTML = '';
|
||||
this.chart = createChart({
|
||||
json: this.props.data,
|
||||
chartType: HourglassChart,
|
||||
chartType: this.getChartType(),
|
||||
renderer: DetailedRenderer,
|
||||
svgSelector: '#chart',
|
||||
indiCallback: (info) => this.props.onSelection(info),
|
||||
@ -192,7 +212,10 @@ export class Chart extends React.PureComponent<ChartProps, {}> {
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: ChartProps) {
|
||||
this.renderChart({initialRender: this.props.data !== prevProps.data});
|
||||
const initialRender =
|
||||
this.props.data !== prevProps.data ||
|
||||
this.props.chartType !== prevProps.chartType;
|
||||
this.renderChart({initialRender});
|
||||
}
|
||||
|
||||
/** Make intl appear in this.context. */
|
||||
|
||||
@ -184,6 +184,16 @@ export class TopBar extends React.Component<
|
||||
}
|
||||
}
|
||||
|
||||
changeView(view: string) {
|
||||
const location = this.props.location;
|
||||
const search = queryString.parse(location.search);
|
||||
if (search.view !== view) {
|
||||
search.view = view;
|
||||
location.search = queryString.stringify(search);
|
||||
this.props.history.push(location);
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.initializeSearchIndex();
|
||||
}
|
||||
@ -259,6 +269,7 @@ export class TopBar extends React.Component<
|
||||
<Icon name="print" />
|
||||
<FormattedMessage id="menu.print" defaultMessage="Print" />
|
||||
</Menu.Item>
|
||||
|
||||
<Dropdown
|
||||
trigger={
|
||||
<div>
|
||||
@ -280,6 +291,34 @@ export class TopBar extends React.Component<
|
||||
</Dropdown.Item>
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
|
||||
<Dropdown
|
||||
trigger={
|
||||
<div>
|
||||
<Icon name="eye" />
|
||||
<FormattedMessage id="menu.view" defaultMessage="View" />
|
||||
</div>
|
||||
}
|
||||
className="item"
|
||||
>
|
||||
<Dropdown.Menu>
|
||||
<Dropdown.Item onClick={() => this.changeView('hourglass')}>
|
||||
<Icon name="hourglass" />
|
||||
<FormattedMessage
|
||||
id="menu.hourglass"
|
||||
defaultMessage="Hourglass chart"
|
||||
/>
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item onClick={() => this.changeView('relatives')}>
|
||||
<Icon name="users" />
|
||||
<FormattedMessage
|
||||
id="menu.relatives"
|
||||
defaultMessage="All relatives"
|
||||
/>
|
||||
</Dropdown.Item>
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
|
||||
<Search
|
||||
onSearchChange={debounce(
|
||||
(_: React.MouseEvent<HTMLElement>, data: SearchProps) =>
|
||||
|
||||
@ -6,6 +6,9 @@
|
||||
"menu.pdf_file": "Plik PDF",
|
||||
"menu.png_file": "Plik PNG",
|
||||
"menu.svg_file": "Plik SVG",
|
||||
"menu.view": "Widok",
|
||||
"menu.hourglass": "Wykres klepsydrowy",
|
||||
"menu.relatives": "Wszyscy krewni",
|
||||
"menu.github": "Źródła na GitHub",
|
||||
"menu.powered_by": "Topola Genealogy",
|
||||
"menu.search.placeholder": "Szukaj osoby",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user