@imerss/inat-components 1.1.2 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/PUBLISH.md +13 -0
- package/README.md +12 -0
- package/dist/index.d.ts +86 -83
- package/dist/index.js +280 -395
- package/dist/standalone.min.js +3 -0
- package/package.json +17 -5
- package/public/standalone.html +55 -0
- package/rollup.config.js +15 -4
- package/src/__shared/api/common-taxa.ts +3 -3
- package/src/__shared/api/favourites.ts +3 -3
- package/src/__shared/api/recent-observations.ts +3 -3
- package/src/__shared/api/summary.ts +1 -0
- package/src/__shared/constants.ts +3 -2
- package/src/__shared/index.ts +1 -1
- package/src/__shared/typings.ts +49 -57
- package/src/__shared/utils/configUtils.ts +7 -7
- package/src/__shared/utils/dateUtils.ts +1 -1
- package/src/components/common-taxa/common-taxa.tsx +22 -57
- package/src/components/error/error.tsx +1 -1
- package/src/components/favourites/favourites.tsx +23 -58
- package/src/components/hooks/hooks.tsx +20 -10
- package/src/components/loader/loader.tsx +1 -1
- package/src/components/observation/observation.tsx +1 -1
- package/src/components/recent-observations/recent-observations.tsx +24 -58
- package/src/components/summary/ObserverList.tsx +3 -3
- package/src/components/summary/summary.tsx +53 -54
- package/src/components/tabs/tabs.tsx +41 -26
- package/src/components/taxon-panel/taxon-panel.tsx +75 -96
- package/src/components/year-dropdown/year-dropdown.tsx +11 -7
- package/src/hooks/useLoadSourceData.ts +81 -0
- package/src/index.ts +1 -1
- package/src/types/index.d.ts +17 -0
- package/src/types/standalone.d.ts +6 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {useEffect, useState} from "react";
|
|
2
|
-
import {
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
import { Feature, TaxonPanelFeatures } from "../../__shared";
|
|
3
3
|
|
|
4
4
|
const defaultTitles = {
|
|
5
|
-
[
|
|
6
|
-
[
|
|
7
|
-
[
|
|
8
|
-
[
|
|
5
|
+
[Feature.recentObservations]: "Recent",
|
|
6
|
+
[Feature.commonTaxa]: "Most common",
|
|
7
|
+
[Feature.favourites]: "Most favourited",
|
|
8
|
+
[Feature.stats]: "General stats"
|
|
9
9
|
};
|
|
10
10
|
|
|
11
11
|
export const useFeatureTitles = (features: any) => {
|
|
@@ -13,12 +13,22 @@ export const useFeatureTitles = (features: any) => {
|
|
|
13
13
|
|
|
14
14
|
useEffect(() => {
|
|
15
15
|
setTitles({
|
|
16
|
-
[
|
|
17
|
-
[
|
|
18
|
-
[
|
|
19
|
-
[
|
|
16
|
+
[Feature.recentObservations]: features.recentObservations?.title || defaultTitles[Feature.recentObservations],
|
|
17
|
+
[Feature.commonTaxa]: features.commonTaxa?.title || defaultTitles[Feature.commonTaxa],
|
|
18
|
+
[Feature.favourites]: features.favourites?.title || defaultTitles[Feature.favourites],
|
|
19
|
+
[Feature.stats]: features.stats?.title || defaultTitles[Feature.stats]
|
|
20
20
|
});
|
|
21
21
|
}, [features]);
|
|
22
22
|
|
|
23
23
|
return titles;
|
|
24
24
|
};
|
|
25
|
+
|
|
26
|
+
export const convertFeaturesToObj = (features: TaxonPanelFeatures) => {
|
|
27
|
+
const featuresObj: any = {};
|
|
28
|
+
features.forEach((row) => {
|
|
29
|
+
const { feature, ...other } = row;
|
|
30
|
+
featuresObj[feature] = other;
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
return featuresObj;
|
|
34
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import LoadingSpinner from "react-spinners/MoonLoader.js";
|
|
2
|
+
import LoadingSpinner from "react-spinners/MoonLoader.js";
|
|
3
3
|
import styles from "../shared/css/general.module.scss";
|
|
4
4
|
|
|
5
5
|
export type LoaderProps = {
|
|
@@ -1,21 +1,22 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import React from "react";
|
|
2
2
|
import {
|
|
3
3
|
formatDate,
|
|
4
4
|
C,
|
|
5
5
|
getRecentObservations,
|
|
6
6
|
RecentObservationData,
|
|
7
7
|
BaseComponentProps,
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
DataSourceEnum,
|
|
9
|
+
GeneralClasses
|
|
10
10
|
} from "../../__shared";
|
|
11
|
-
import {Observation} from "../observation/observation";
|
|
11
|
+
import { Observation } from "../observation/observation";
|
|
12
12
|
import Loader from "../loader/loader";
|
|
13
|
-
import {NoResults} from "../no-results/no-results";
|
|
13
|
+
import { NoResults } from "../no-results/no-results";
|
|
14
|
+
import { useLoadSourceData } from "../../hooks/useLoadSourceData";
|
|
14
15
|
import styles from "../shared/css/general.module.scss";
|
|
15
16
|
|
|
16
17
|
export type RecentObservationsProps = BaseComponentProps;
|
|
17
18
|
|
|
18
|
-
export const RecentObservationLabel = (obs: RecentObservationData & { classes:
|
|
19
|
+
export const RecentObservationLabel = (obs: RecentObservationData & { classes: GeneralClasses }) => (
|
|
19
20
|
<div className={styles.obsLabel}>
|
|
20
21
|
<h3 className={obs.classes?.observationLabelTitle}>{obs.taxonCommonName || obs.taxonName}</h3>
|
|
21
22
|
<div className={obs.classes?.observationLabelDate}>{formatDate(obs.obsDate)}</div>
|
|
@@ -33,59 +34,23 @@ export const RecentObservationLabel = (obs: RecentObservationData & { classes: B
|
|
|
33
34
|
export const RecentObservations = ({
|
|
34
35
|
taxonId,
|
|
35
36
|
placeId,
|
|
36
|
-
data,
|
|
37
37
|
dataUrl,
|
|
38
|
-
source = DataSource.autoLoad,
|
|
39
|
-
perPage = C.PER_PAGE,
|
|
40
|
-
itemWidth = C.DEFAULT_ITEM_WIDTH,
|
|
41
38
|
components,
|
|
42
39
|
className,
|
|
43
|
-
|
|
44
|
-
tabDesc
|
|
40
|
+
generalClasses,
|
|
41
|
+
tabDesc,
|
|
42
|
+
source = DataSourceEnum.autoLoad,
|
|
43
|
+
numResults = C.NUM_RESULTS,
|
|
44
|
+
itemWidth = C.DEFAULT_ITEM_WIDTH
|
|
45
45
|
}: RecentObservationsProps) => {
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
console.error("Please supply a `taxonId` prop for the `autoLoad` source prop option.");
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
if (!placeId) {
|
|
58
|
-
console.error("Please supply a `placeId` prop for the `autoLoad` source prop option.");
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
(async () => {
|
|
63
|
-
setLoading(true);
|
|
64
|
-
const obs = await getRecentObservations({taxonId, placeId, perPage});
|
|
65
|
-
setObservations(obs.results);
|
|
66
|
-
setLoading(false);
|
|
67
|
-
})();
|
|
68
|
-
}, [source, taxonId, placeId, perPage]);
|
|
69
|
-
|
|
70
|
-
useEffect(() => {
|
|
71
|
-
if (source !== DataSource.url) {
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (!dataUrl) {
|
|
76
|
-
console.error("Please supply a `dataUrl` prop for the `url` source prop option.");
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
(async () => {
|
|
81
|
-
setLoading(true);
|
|
82
|
-
const obs = await fetch(dataUrl);
|
|
83
|
-
const json = await obs.json();
|
|
84
|
-
setObservations(json.results);
|
|
85
|
-
setLoading(false);
|
|
86
|
-
})();
|
|
87
|
-
}, [source, dataUrl]);
|
|
88
|
-
|
|
46
|
+
const { loading, results: observations } = useLoadSourceData({
|
|
47
|
+
taxonId,
|
|
48
|
+
placeId,
|
|
49
|
+
numResults,
|
|
50
|
+
dataUrl,
|
|
51
|
+
source,
|
|
52
|
+
action: getRecentObservations
|
|
53
|
+
});
|
|
89
54
|
const Load = components?.loader ? components.loader as any : Loader;
|
|
90
55
|
const Label = components?.label ? components.label as any : RecentObservationLabel;
|
|
91
56
|
|
|
@@ -94,9 +59,10 @@ export const RecentObservations = ({
|
|
|
94
59
|
componentClasses += ` ${className}`;
|
|
95
60
|
}
|
|
96
61
|
let descClasses = styles.tabDesc;
|
|
97
|
-
if (
|
|
98
|
-
descClasses += ` ${
|
|
62
|
+
if (generalClasses?.tabDesc) {
|
|
63
|
+
descClasses += ` ${generalClasses.tabDesc}`;
|
|
99
64
|
}
|
|
65
|
+
|
|
100
66
|
return (
|
|
101
67
|
<div className={componentClasses}>
|
|
102
68
|
{tabDesc && <p className={descClasses}>{tabDesc}</p>}
|
|
@@ -109,7 +75,7 @@ export const RecentObservations = ({
|
|
|
109
75
|
imageUrl={obs.imageUrl.replace(/square/, "medium")}
|
|
110
76
|
itemWidth={itemWidth}
|
|
111
77
|
linkUrl={obs.obsUrl}>
|
|
112
|
-
<Label {...obs} classes={
|
|
78
|
+
<Label {...obs} classes={generalClasses} />
|
|
113
79
|
</Observation>
|
|
114
80
|
))}
|
|
115
81
|
</div>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import {numberWithCommas, Observer, C} from "../../__shared";
|
|
2
|
+
import { numberWithCommas, Observer, C, GeneralClasses } from "../../__shared";
|
|
3
3
|
import styles from "./summary.module.scss";
|
|
4
4
|
|
|
5
|
-
export const ObserverList = ({observers, className = ''}: { observers: Observer[], className?: string }) => {
|
|
5
|
+
export const ObserverList = ({ observers, generalClasses, className = '' }: { observers: Observer[], generalClasses?: GeneralClasses, className?: string }) => {
|
|
6
6
|
let classes = styles.top;
|
|
7
7
|
if (className) {
|
|
8
8
|
classes += ` ${className}`;
|
|
@@ -15,7 +15,7 @@ export const ObserverList = ({observers, className = ''}: { observers: Observer[
|
|
|
15
15
|
{obs.iconUrl ? <img src={obs.iconUrl || ""} className={styles.avatar} alt="User icon"/> :
|
|
16
16
|
<div className={styles.noAvatar}/>}
|
|
17
17
|
<h3>{obs.userName}</h3>
|
|
18
|
-
<label>{numberWithCommas(obs.numObservations)}</label>
|
|
18
|
+
<label className={generalClasses?.observationCount}>{numberWithCommas(obs.numObservations)}</label>
|
|
19
19
|
</a>
|
|
20
20
|
</li>
|
|
21
21
|
))}
|
|
@@ -1,76 +1,69 @@
|
|
|
1
|
-
import React
|
|
2
|
-
import {
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { numberWithCommas, DataSource, getSummary, GeneralClasses } from "../../__shared";
|
|
3
3
|
import { ObserverList } from './ObserverList';
|
|
4
4
|
import Loader from "../loader/loader";
|
|
5
|
-
import {SeasonalityGraph} from "../seasonality-graph/seasonality-graph";
|
|
5
|
+
import { SeasonalityGraph } from "../seasonality-graph/seasonality-graph";
|
|
6
6
|
import styles from "./summary.module.scss";
|
|
7
|
+
import { useLoadSourceData } from "../../hooks/useLoadSourceData";
|
|
7
8
|
|
|
8
9
|
export type SummaryProps = {
|
|
9
|
-
source: DataSource;
|
|
10
10
|
taxonId: number;
|
|
11
11
|
placeId: number;
|
|
12
|
-
year:
|
|
13
|
-
|
|
12
|
+
year: number;
|
|
13
|
+
source: DataSource;
|
|
14
14
|
dataUrl?: string;
|
|
15
|
-
|
|
15
|
+
generalClasses?: GeneralClasses;
|
|
16
|
+
className?: string;
|
|
16
17
|
tabDesc?: string;
|
|
18
|
+
observersListClass?: string;
|
|
19
|
+
statsCountSummaryClass?: string;
|
|
17
20
|
};
|
|
18
21
|
|
|
19
|
-
export const Summary = ({
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
22
|
+
export const Summary = ({
|
|
23
|
+
source,
|
|
24
|
+
dataUrl,
|
|
25
|
+
taxonId,
|
|
26
|
+
placeId,
|
|
27
|
+
year,
|
|
28
|
+
generalClasses,
|
|
29
|
+
className,
|
|
30
|
+
tabDesc,
|
|
31
|
+
observersListClass,
|
|
32
|
+
statsCountSummaryClass
|
|
33
|
+
}: SummaryProps) => {
|
|
34
|
+
const { loading, results: summaryData } = useLoadSourceData({
|
|
35
|
+
year,
|
|
36
|
+
taxonId,
|
|
37
|
+
placeId,
|
|
38
|
+
dataUrl,
|
|
39
|
+
source,
|
|
40
|
+
isSummaryData: true,
|
|
35
41
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
setLoading(false);
|
|
41
|
-
})();
|
|
42
|
-
}, [source, year, placeId, taxonId]);
|
|
42
|
+
// shim to standardize the response from the `action` method with a top-level `results` property. This
|
|
43
|
+
// just lets the useLoadSourceData hook work for all tab components
|
|
44
|
+
action: async (data) => {
|
|
45
|
+
const results = await getSummary(data);
|
|
43
46
|
|
|
44
|
-
|
|
45
|
-
if (source !== DataSource.url) {
|
|
46
|
-
return;
|
|
47
|
+
return { results };
|
|
47
48
|
}
|
|
48
|
-
|
|
49
|
-
if (!dataUrl) {
|
|
50
|
-
console.error("Please supply a `dataUrl` prop for the `url` source prop option.");
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
(async () => {
|
|
55
|
-
setLoading(true);
|
|
56
|
-
const obs = await fetch(dataUrl);
|
|
57
|
-
const json = await obs.json();
|
|
58
|
-
setSummaryData(json);
|
|
59
|
-
setLoading(false);
|
|
60
|
-
})();
|
|
61
|
-
}, [source, dataUrl]);
|
|
49
|
+
});
|
|
62
50
|
|
|
63
51
|
let countSummaryClasses = styles.countSummary;
|
|
64
|
-
if (
|
|
65
|
-
countSummaryClasses += ` ${
|
|
52
|
+
if (statsCountSummaryClass) {
|
|
53
|
+
countSummaryClasses += ` ${statsCountSummaryClass}`;
|
|
66
54
|
}
|
|
67
55
|
let descClasses = styles.tabDesc;
|
|
68
|
-
if (
|
|
69
|
-
descClasses += ` ${
|
|
56
|
+
if (generalClasses?.tabDesc) {
|
|
57
|
+
descClasses += ` ${generalClasses.tabDesc}`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
let classList = styles.summaryPage;
|
|
61
|
+
if (className) {
|
|
62
|
+
classList += ` ${className}`;
|
|
70
63
|
}
|
|
71
64
|
|
|
72
65
|
return (
|
|
73
|
-
<div className={
|
|
66
|
+
<div className={classList}>
|
|
74
67
|
{tabDesc && <p className={descClasses}>{tabDesc}</p>}
|
|
75
68
|
<Loader loading={loading} />
|
|
76
69
|
<ul className={countSummaryClasses}>
|
|
@@ -84,9 +77,15 @@ export const Summary = ({ source, data, dataUrl, taxonId, placeId, year, classes
|
|
|
84
77
|
</div>
|
|
85
78
|
<div className={styles.observersBlock}>
|
|
86
79
|
<h1>Top observers</h1>
|
|
87
|
-
{summaryData.observers?.top &&
|
|
80
|
+
{summaryData.observers?.top && (
|
|
81
|
+
<ObserverList
|
|
82
|
+
observers={summaryData.observers.top}
|
|
83
|
+
className={observersListClass}
|
|
84
|
+
generalClasses={generalClasses}
|
|
85
|
+
/>
|
|
86
|
+
)}
|
|
88
87
|
</div>
|
|
89
88
|
</section>
|
|
90
89
|
</div>
|
|
91
90
|
);
|
|
92
|
-
}
|
|
91
|
+
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { Feature, TaxonPanelFeatures } from "../../__shared";
|
|
3
3
|
import styles from "./tabs.module.scss";
|
|
4
|
-
import {useFeatureTitles} from "../hooks/hooks";
|
|
4
|
+
import { useFeatureTitles } from "../hooks/hooks";
|
|
5
5
|
|
|
6
6
|
export type TabsProps = {
|
|
7
|
-
selectedTab:
|
|
8
|
-
onChangeTab: (tab:
|
|
9
|
-
features:
|
|
7
|
+
selectedTab: Feature;
|
|
8
|
+
onChangeTab: (tab: Feature) => void;
|
|
9
|
+
features: TaxonPanelFeatures;
|
|
10
10
|
className?: string;
|
|
11
11
|
}
|
|
12
12
|
|
|
@@ -18,31 +18,46 @@ const Tabs = ({ selectedTab, onChangeTab, features, className }: TabsProps) => {
|
|
|
18
18
|
classes += ` ${className}`;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
<li
|
|
25
|
-
|
|
26
|
-
{
|
|
21
|
+
const items = features.map((item) => {
|
|
22
|
+
if (item.feature == Feature.recentObservations) {
|
|
23
|
+
return (
|
|
24
|
+
<li key={Feature.recentObservations}
|
|
25
|
+
onClick={() => onChangeTab(Feature.recentObservations)}
|
|
26
|
+
className={selectedTab === Feature.recentObservations ? styles.selected : ""}>
|
|
27
|
+
{titles[Feature.recentObservations]}
|
|
27
28
|
</li>
|
|
28
|
-
)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
{
|
|
29
|
+
);
|
|
30
|
+
} else if (item.feature == Feature.commonTaxa) {
|
|
31
|
+
return (
|
|
32
|
+
<li key={Feature.commonTaxa}
|
|
33
|
+
onClick={() => onChangeTab(Feature.commonTaxa)}
|
|
34
|
+
className={selectedTab === Feature.commonTaxa ? styles.selected : ""}>
|
|
35
|
+
{titles[Feature.commonTaxa]}
|
|
33
36
|
</li>
|
|
34
|
-
)
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
{
|
|
37
|
+
);
|
|
38
|
+
} else if (item.feature == Feature.favourites) {
|
|
39
|
+
return (
|
|
40
|
+
<li key={Feature.favourites}
|
|
41
|
+
onClick={() => onChangeTab(Feature.favourites)}
|
|
42
|
+
className={selectedTab === Feature.favourites ? styles.selected : ""}>
|
|
43
|
+
{titles[Feature.favourites]}
|
|
39
44
|
</li>
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
45
|
+
);
|
|
46
|
+
} else if (item.feature == Feature.stats) {
|
|
47
|
+
return (
|
|
48
|
+
<li key={Feature.stats} onClick={() => onChangeTab(Feature.stats)} className={selectedTab === Feature.stats ? styles.selected : ""}>
|
|
49
|
+
{titles[Feature.stats]}
|
|
44
50
|
</li>
|
|
45
|
-
)
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// possibly a user has included something wacky
|
|
55
|
+
return undefined;
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<ul className={classes}>
|
|
60
|
+
{items}
|
|
46
61
|
</ul>
|
|
47
62
|
);
|
|
48
63
|
}
|
|
@@ -1,127 +1,106 @@
|
|
|
1
|
-
import React, {useState} from "react";
|
|
2
|
-
import {
|
|
3
|
-
import {RecentObservations
|
|
4
|
-
import {CommonTaxa} from '../common-taxa/common-taxa';
|
|
5
|
-
import {Favourites} from "../favourites/favourites"
|
|
6
|
-
import {Summary} from "../summary/summary";
|
|
1
|
+
import React, { useMemo, useState } from "react";
|
|
2
|
+
import { C, getSourceFile, DataSource, DataSourceEnum, Feature, GeneralClasses, TaxonPanelFeatures } from "../../__shared";
|
|
3
|
+
import { RecentObservations } from '../recent-observations/recent-observations';
|
|
4
|
+
import { CommonTaxa } from '../common-taxa/common-taxa';
|
|
5
|
+
import { Favourites } from "../favourites/favourites"
|
|
6
|
+
import { Summary } from "../summary/summary";
|
|
7
7
|
import Years from "../year-dropdown/year-dropdown";
|
|
8
8
|
import Tabs from "../tabs/tabs";
|
|
9
|
-
import {useFeatureTitles} from "../hooks/hooks";
|
|
10
|
-
import styles from
|
|
9
|
+
import { convertFeaturesToObj, useFeatureTitles } from "../hooks/hooks";
|
|
10
|
+
import styles from "../shared/css/general.module.scss";
|
|
11
11
|
|
|
12
12
|
export type TaxonPanelProps = {
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
taxon: {
|
|
14
|
+
id: number;
|
|
15
|
+
str: string;
|
|
16
|
+
};
|
|
17
|
+
place: {
|
|
18
|
+
id: number;
|
|
19
|
+
str: string;
|
|
20
|
+
}
|
|
21
|
+
dataSourceBaseUrl: string;
|
|
15
22
|
dataSource: DataSource;
|
|
16
|
-
|
|
17
|
-
|
|
23
|
+
features: TaxonPanelFeatures;
|
|
24
|
+
generalClasses: GeneralClasses;
|
|
18
25
|
itemWidth?: number;
|
|
19
|
-
classes?: BaseClasses;
|
|
20
|
-
tabDescs?: TabDescs;
|
|
21
26
|
}
|
|
22
27
|
|
|
23
|
-
const TaxonPanel = ({
|
|
24
|
-
const [tab, setTab] = useState(
|
|
28
|
+
const TaxonPanel = ({ taxon, place, features, dataSource, dataSourceBaseUrl, itemWidth, generalClasses }: TaxonPanelProps) => {
|
|
29
|
+
const [tab, setTab] = useState(features[0].feature);
|
|
25
30
|
const [year, setYear] = useState<string>("all");
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
31
|
+
const featuresObj = useMemo(() => {
|
|
32
|
+
return convertFeaturesToObj(features);
|
|
33
|
+
}, [features]);
|
|
34
|
+
const titles = useFeatureTitles(featuresObj);
|
|
35
|
+
|
|
36
|
+
const getProps = (feature: Feature): any => {
|
|
37
|
+
const props: any = {
|
|
38
|
+
source: dataSource,
|
|
39
|
+
itemWidth,
|
|
40
|
+
tabDesc: featuresObj[feature]?.desc,
|
|
41
|
+
generalClasses,
|
|
42
|
+
className: featuresObj[feature]?.className
|
|
43
|
+
};
|
|
44
|
+
if (dataSource === DataSourceEnum.url) {
|
|
45
|
+
props.dataUrl = dataSourceBaseUrl + '/' + getSourceFile(feature, taxon, place, year);
|
|
46
|
+
} else {
|
|
47
|
+
props.placeId = place.id;
|
|
48
|
+
props.taxonId = taxon.id;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (feature !== Feature.stats) {
|
|
52
|
+
props.numResults = featuresObj[feature]?.numResults;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (feature !== Feature.recentObservations) {
|
|
56
|
+
props.year = year;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return props;
|
|
60
|
+
};
|
|
29
61
|
|
|
30
|
-
console.log("..." , classes);
|
|
31
62
|
const getCurrentTab = () => {
|
|
32
63
|
switch (tab) {
|
|
33
|
-
case
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
};
|
|
40
|
-
if (dataSource === DataSource.url) {
|
|
41
|
-
props.dataUrl = baseUrl + '/' + getSourceFile(Feature.recentObservations, taxonInfo, placeInfo);
|
|
42
|
-
} else {
|
|
43
|
-
props.placeId = placeId;
|
|
44
|
-
props.taxonId = taxonId;
|
|
45
|
-
}
|
|
46
|
-
return (
|
|
47
|
-
<RecentObservations {...props} />
|
|
48
|
-
);
|
|
49
|
-
}
|
|
64
|
+
case Feature.recentObservations:
|
|
65
|
+
return <RecentObservations {...getProps(Feature.recentObservations)} />;
|
|
66
|
+
case Feature.commonTaxa:
|
|
67
|
+
return <CommonTaxa {...getProps(Feature.commonTaxa)} />;
|
|
68
|
+
case Feature.favourites:
|
|
69
|
+
return <Favourites {...getProps(Feature.favourites)} />;
|
|
50
70
|
|
|
51
|
-
case
|
|
52
|
-
const props: any = {
|
|
53
|
-
classes,
|
|
54
|
-
source: dataSource,
|
|
55
|
-
itemWidth,
|
|
56
|
-
year,
|
|
57
|
-
tabDesc: tabDescs?.mostCommonDesc
|
|
58
|
-
};
|
|
59
|
-
if (dataSource === DataSource.url) {
|
|
60
|
-
props.dataUrl = baseUrl + '/' + getSourceFile(Feature.commonTaxa, taxonInfo, placeInfo, year);
|
|
61
|
-
} else {
|
|
62
|
-
props.placeId = placeId;
|
|
63
|
-
props.taxonId = taxonId;
|
|
64
|
-
}
|
|
71
|
+
case Feature.stats:
|
|
65
72
|
return (
|
|
66
|
-
<
|
|
73
|
+
<Summary
|
|
74
|
+
{...getProps(Feature.stats)}
|
|
75
|
+
observersListClass={featuresObj[Feature.stats]?.observersListClass}
|
|
76
|
+
statsCountSummaryClass={featuresObj[Feature.stats]?.statsCountSummaryClass}
|
|
77
|
+
/>
|
|
67
78
|
);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
case Tab.favourites: {
|
|
71
|
-
const props: any = {
|
|
72
|
-
classes,
|
|
73
|
-
itemWidth,
|
|
74
|
-
source: dataSource,
|
|
75
|
-
year,
|
|
76
|
-
tabDesc: tabDescs?.mostFavouritedDesc
|
|
77
|
-
};
|
|
78
|
-
if (dataSource === DataSource.url) {
|
|
79
|
-
props.dataUrl = baseUrl + '/' + getSourceFile(Feature.favourites, taxonInfo, placeInfo, year);
|
|
80
|
-
} else {
|
|
81
|
-
props.placeId = placeId;
|
|
82
|
-
props.taxonId = taxonId;
|
|
83
|
-
}
|
|
84
|
-
return (
|
|
85
|
-
<Favourites {...props} />
|
|
86
|
-
);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
case Tab.stats: {
|
|
90
|
-
const props: any = {
|
|
91
|
-
classes,
|
|
92
|
-
source: dataSource,
|
|
93
|
-
year,
|
|
94
|
-
tabDesc: tabDescs?.generalStatsDesc
|
|
95
|
-
};
|
|
96
|
-
if (dataSource === DataSource.url) {
|
|
97
|
-
props.dataUrl = baseUrl + '/' + getSourceFile(Feature.stats, taxonInfo, placeInfo, year);
|
|
98
|
-
} else {
|
|
99
|
-
props.placeId = placeId;
|
|
100
|
-
props.taxonId = taxonId;
|
|
101
|
-
}
|
|
102
|
-
return <Summary {...props} />;
|
|
103
|
-
}
|
|
104
79
|
}
|
|
105
80
|
};
|
|
106
81
|
|
|
107
|
-
const tabsClass =
|
|
108
|
-
const yearDropdownClass =
|
|
82
|
+
const tabsClass = generalClasses?.tabsElement || '';
|
|
83
|
+
const yearDropdownClass = generalClasses?.yearsDropdown || '';
|
|
109
84
|
|
|
110
85
|
return (
|
|
111
86
|
<div className={styles.page}>
|
|
112
|
-
<Tabs
|
|
87
|
+
{features.length > 1 && <Tabs
|
|
113
88
|
selectedTab={tab}
|
|
114
89
|
onChangeTab={setTab}
|
|
115
|
-
features={
|
|
90
|
+
features={features}
|
|
116
91
|
className={tabsClass}
|
|
117
|
-
/>
|
|
92
|
+
/>}
|
|
118
93
|
<div>
|
|
119
|
-
{tab !==
|
|
94
|
+
{tab !== Feature.recentObservations && (
|
|
120
95
|
<div style={{ float: "right" }} className={yearDropdownClass}>
|
|
121
|
-
<Years
|
|
96
|
+
<Years
|
|
97
|
+
value={year}
|
|
98
|
+
numYears={featuresObj[tab]?.numYears || C.DEFAULT_NUM_YEARS}
|
|
99
|
+
onChange={setYear}
|
|
100
|
+
/>
|
|
122
101
|
</div>
|
|
123
102
|
)}
|
|
124
|
-
<h1 className={
|
|
103
|
+
<h1 className={generalClasses?.pageHeadings}>{titles[tab]}</h1>
|
|
125
104
|
</div>
|
|
126
105
|
{getCurrentTab()}
|
|
127
106
|
</div>
|