@imerss/inat-components 1.1.1 → 1.1.2

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/dist/index.d.ts CHANGED
@@ -1,12 +1,3 @@
1
- declare const TaxonPanel: ({ taxonId, placeId, dataSource, config, baseURL }: any) => JSX.Element;
2
-
3
- declare type ObservationProps = {
4
- imageUrl: string;
5
- linkUrl: string;
6
- children: any;
7
- };
8
- declare const Observation: ({ imageUrl, linkUrl, children }: ObservationProps) => JSX.Element;
9
-
10
1
  declare type CommonTaxaCallProps = {
11
2
  year: string | number;
12
3
  taxonId: string | number;
@@ -132,6 +123,17 @@ declare enum Tab {
132
123
  favourites = "favourites",
133
124
  stats = "stats"
134
125
  }
126
+ declare type BaseClasses = {
127
+ tabs?: string;
128
+ yearsDropdown?: string;
129
+ pageHeadings?: string;
130
+ observationLabelTitle?: string;
131
+ observationLabelDate?: string;
132
+ observationLabelName?: string;
133
+ observersList?: string;
134
+ statsCountSummary?: string;
135
+ tabDesc?: string;
136
+ };
135
137
  declare type BaseComponentProps = {
136
138
  taxonId?: number;
137
139
  placeId?: number;
@@ -141,11 +143,14 @@ declare type BaseComponentProps = {
141
143
  data?: any;
142
144
  dataUrl?: string;
143
145
  className?: string;
146
+ itemWidth?: number;
144
147
  components?: {
145
148
  label?: any;
146
149
  error?: any;
147
150
  loader?: any;
148
151
  };
152
+ classes?: BaseClasses;
153
+ tabDesc?: string;
149
154
  };
150
155
  declare type TaxaConfig = {
151
156
  label: string;
@@ -157,8 +162,14 @@ declare type PlaceConfig = {
157
162
  short: string;
158
163
  placeId: number;
159
164
  };
165
+ declare type TabDescs = {
166
+ recentDesc?: string;
167
+ mostCommonDesc?: string;
168
+ mostFavouritedDesc?: string;
169
+ generalStatsDesc?: string;
170
+ };
160
171
 
161
- declare const getSourceFile: (api: Feature, taxonInfo: TaxaConfig, placeInfo: PlaceConfig, year?: string | number) => string;
172
+ declare const getSourceFile: (api: Feature, taxonInfo?: TaxaConfig, placeInfo?: PlaceConfig, year?: string | number) => string;
162
173
 
163
174
  declare const getCurrentYear: () => number;
164
175
  declare const formatDate: (date: string, dateFormat?: string) => any;
@@ -169,35 +180,64 @@ declare const BASE_API_URL = "https://api.inaturalist.org";
169
180
  declare const PER_PAGE = 100;
170
181
  declare const BASE_URL = "https://www.inaturalist.org";
171
182
  declare const ERROR_LOG_FILENAME = "inat-components-error.log";
183
+ declare const DEFAULT_ITEM_WIDTH = 150;
172
184
 
173
185
  declare const constants_BASE_API_URL: typeof BASE_API_URL;
174
186
  declare const constants_PER_PAGE: typeof PER_PAGE;
175
187
  declare const constants_BASE_URL: typeof BASE_URL;
176
188
  declare const constants_ERROR_LOG_FILENAME: typeof ERROR_LOG_FILENAME;
189
+ declare const constants_DEFAULT_ITEM_WIDTH: typeof DEFAULT_ITEM_WIDTH;
177
190
  declare namespace constants {
178
191
  export {
179
192
  constants_BASE_API_URL as BASE_API_URL,
180
193
  constants_PER_PAGE as PER_PAGE,
181
194
  constants_BASE_URL as BASE_URL,
182
195
  constants_ERROR_LOG_FILENAME as ERROR_LOG_FILENAME,
196
+ constants_DEFAULT_ITEM_WIDTH as DEFAULT_ITEM_WIDTH,
183
197
  };
184
198
  }
185
199
 
200
+ declare type TaxonPanelProps = {
201
+ taxonId: number;
202
+ placeId: number;
203
+ dataSource: DataSource;
204
+ config: ConfigFile;
205
+ baseUrl: string;
206
+ itemWidth?: number;
207
+ classes?: BaseClasses;
208
+ tabDescs?: TabDescs;
209
+ };
210
+ declare const TaxonPanel: ({ taxonId, placeId, dataSource, config, baseUrl, itemWidth, classes, tabDescs }: TaxonPanelProps) => JSX.Element;
211
+
212
+ declare type ObservationProps = {
213
+ imageUrl: string;
214
+ linkUrl: string;
215
+ itemWidth: number;
216
+ children: any;
217
+ };
218
+ declare const Observation: ({ imageUrl, linkUrl, itemWidth, children }: ObservationProps) => JSX.Element;
219
+
186
220
  declare type CommonTaxaProps = BaseComponentProps & {
187
221
  year: string | number;
188
222
  };
189
- declare const CommonTaxaLabel: (data: CommonTaxData) => JSX.Element;
190
- declare const CommonTaxa: ({ year, source, taxonId, placeId, perPage, data, dataUrl, components, className }: CommonTaxaProps) => JSX.Element;
223
+ declare const CommonTaxaLabel: (data: CommonTaxData & {
224
+ classes: BaseClasses;
225
+ }) => JSX.Element;
226
+ declare const CommonTaxa: ({ year, source, taxonId, placeId, perPage, itemWidth, data, dataUrl, components, classes, className, tabDesc }: CommonTaxaProps) => JSX.Element;
191
227
 
192
228
  declare type RecentObservationsProps = BaseComponentProps;
193
- declare const RecentObservationLabel: (obs: RecentObservationData) => JSX.Element;
194
- declare const RecentObservations: ({ taxonId, placeId, data, dataUrl, source, perPage, components, className }: RecentObservationsProps) => JSX.Element;
229
+ declare const RecentObservationLabel: (obs: RecentObservationData & {
230
+ classes: BaseClasses;
231
+ }) => JSX.Element;
232
+ declare const RecentObservations: ({ taxonId, placeId, data, dataUrl, source, perPage, itemWidth, components, className, classes, tabDesc }: RecentObservationsProps) => JSX.Element;
195
233
 
196
234
  declare type FavouritesProps = BaseComponentProps & {
197
235
  year: string | number;
198
236
  };
199
- declare const FavouritesLabel: (data: FavouritesData) => JSX.Element;
200
- declare const Favourites: ({ year, source, taxonId, placeId, data, dataUrl, components, className, perPage }: FavouritesProps) => JSX.Element;
237
+ declare const FavouritesLabel: (data: FavouritesData & {
238
+ classes: BaseClasses;
239
+ }) => JSX.Element;
240
+ declare const Favourites: ({ year, source, taxonId, placeId, data, dataUrl, components, className, tabDesc, classes, itemWidth, perPage }: FavouritesProps) => JSX.Element;
201
241
 
202
242
  declare const SeasonalityGraph: ({ data }: any) => JSX.Element;
203
243
 
@@ -208,7 +248,9 @@ declare type SummaryProps = {
208
248
  year: string | number;
209
249
  data?: FavouritesRespData;
210
250
  dataUrl?: string;
251
+ classes?: BaseClasses;
252
+ tabDesc?: string;
211
253
  };
212
- declare const Summary: ({ source, data, dataUrl, taxonId, placeId, year }: SummaryProps) => JSX.Element;
254
+ declare const Summary: ({ source, data, dataUrl, taxonId, placeId, year, classes, tabDesc }: SummaryProps) => JSX.Element;
213
255
 
214
- export { BaseComponentProps, constants as C, CommonTaxData, CommonTaxa, CommonTaxaCallProps, CommonTaxaLabel, CommonTaxaProps, CommonTaxaRespData, ConfigFile, DataSource, Favourites, FavouritesCallProps, FavouritesData, FavouritesLabel, FavouritesProps, FavouritesRespData, Feature, Observation, ObservationProps, Observer, PlaceConfig, RecentObservationData, RecentObservationLabel, RecentObservations, RecentObservationsCallProps, RecentObservationsProps, RecentObservationsRespData, SeasonalityData, SeasonalityGraph, Summary, SummaryApiProps, SummaryData, SummaryProps, Tab, TaxaConfig, TaxonPanel as default, formatDate, getCommonTaxa, getCurrentYear, getFavourites, getObservationSummary, getObserverSummary, getRecentObservations, getSeasonalityData, getSourceFile, getSummary, numberWithCommas };
256
+ export { BaseClasses, BaseComponentProps, constants as C, CommonTaxData, CommonTaxa, CommonTaxaCallProps, CommonTaxaLabel, CommonTaxaProps, CommonTaxaRespData, ConfigFile, DataSource, Favourites, FavouritesCallProps, FavouritesData, FavouritesLabel, FavouritesProps, FavouritesRespData, Feature, Observation, ObservationProps, Observer, PlaceConfig, RecentObservationData, RecentObservationLabel, RecentObservations, RecentObservationsCallProps, RecentObservationsProps, RecentObservationsRespData, SeasonalityData, SeasonalityGraph, Summary, SummaryApiProps, SummaryData, SummaryProps, Tab, TabDescs, TaxaConfig, TaxonPanelProps, TaxonPanel as default, formatDate, getCommonTaxa, getCurrentYear, getFavourites, getObservationSummary, getObserverSummary, getRecentObservations, getSeasonalityData, getSourceFile, getSummary, numberWithCommas };
package/dist/index.js CHANGED
@@ -73,13 +73,15 @@ var BASE_API_URL = 'https://api.inaturalist.org';
73
73
  var PER_PAGE = 100;
74
74
  var BASE_URL = "https://www.inaturalist.org"; // /people/name_here
75
75
  var ERROR_LOG_FILENAME = "inat-components-error.log";
76
+ var DEFAULT_ITEM_WIDTH = 150;
76
77
 
77
78
  var constants = /*#__PURE__*/Object.freeze({
78
79
  __proto__: null,
79
80
  BASE_API_URL: BASE_API_URL,
80
81
  PER_PAGE: PER_PAGE,
81
82
  BASE_URL: BASE_URL,
82
- ERROR_LOG_FILENAME: ERROR_LOG_FILENAME
83
+ ERROR_LOG_FILENAME: ERROR_LOG_FILENAME,
84
+ DEFAULT_ITEM_WIDTH: DEFAULT_ITEM_WIDTH
83
85
  });
84
86
 
85
87
  var getCommonTaxa = function (_a) {
@@ -142,12 +144,7 @@ var getFavourites = function (_a) {
142
144
  if (year !== "all") {
143
145
  url += "&d1=".concat(year, "-01-01&d2=").concat(year, "-12-31");
144
146
  }
145
- return [4 /*yield*/, fetch$1(url, {
146
- // method: 'GET',
147
- // headers: {
148
- // Accept: 'application/json'
149
- // }
150
- })];
147
+ return [4 /*yield*/, fetch$1(url)];
151
148
  case 1:
152
149
  response = _b.sent();
153
150
  return [4 /*yield*/, response.json()];
@@ -164,18 +161,15 @@ var getFavourites = function (_a) {
164
161
  });
165
162
  return [2 /*return*/, {
166
163
  totalResults: resp.total_results,
167
- results: sortedTaxa.map(function (row) {
168
- var _a, _b;
169
- return ({
170
- id: row.id,
171
- imageUrl: ((_b = (_a = row.taxon) === null || _a === void 0 ? void 0 : _a.default_photo) === null || _b === void 0 ? void 0 : _b.square_url) || "",
172
- obsDate: row.observed_on_string,
173
- obsUrl: row.uri,
174
- taxonName: row.taxon.name || "",
175
- taxonCommonName: row.taxon.preferred_common_name,
176
- numFaves: row.faves.length
177
- });
178
- })
164
+ results: sortedTaxa.map(function (row) { return ({
165
+ id: row.id,
166
+ imageUrl: row.observation_photos[0].photo.url || "",
167
+ obsDate: row.observed_on_string,
168
+ obsUrl: row.uri,
169
+ taxonName: row.taxon.name || "",
170
+ taxonCommonName: row.taxon.preferred_common_name,
171
+ numFaves: row.faves.length
172
+ }); })
179
173
  }];
180
174
  }
181
175
  });
@@ -336,6 +330,9 @@ var Tab;
336
330
  * Note that right now the source filenames aren't configurable.
337
331
  */
338
332
  var getSourceFile = function (api, taxonInfo, placeInfo, year) {
333
+ if (!taxonInfo || !placeInfo) {
334
+ return '';
335
+ }
339
336
  var yearStr = year === "all" ? "allyears" : year;
340
337
  var filename = "";
341
338
  if (api === Feature.recentObservations) {
@@ -401,13 +398,13 @@ var styles$5 = {"obs":"observation-module_obs__-5hGT","image":"observation-modul
401
398
  styleInject(css_248z$5);
402
399
 
403
400
  var Observation = function (_a) {
404
- var imageUrl = _a.imageUrl, linkUrl = _a.linkUrl, children = _a.children;
401
+ var imageUrl = _a.imageUrl, linkUrl = _a.linkUrl, itemWidth = _a.itemWidth, children = _a.children;
405
402
  var gotoObservation = useCallback(function () {
406
403
  window.open(linkUrl);
407
404
  }, [linkUrl]);
408
405
  return (jsxs("article", __assign({ className: styles$5.obs, onClick: gotoObservation }, { children: [jsx("div", { style: {
409
- width: 200,
410
- height: 200,
406
+ width: itemWidth,
407
+ height: itemWidth,
411
408
  overflow: "hidden",
412
409
  borderRadius: 5,
413
410
  background: "url(".concat(imageUrl, ") 50% 50% no-repeat"),
@@ -416,8 +413,8 @@ var Observation = function (_a) {
416
413
  } }), children] })));
417
414
  };
418
415
 
419
- var css_248z$4 = ".general-module_page__ziNB9 {\n padding: 80px 10px 10px;\n margin: 0 auto;\n max-width: 1100px; }\n .general-module_page__ziNB9 h1 {\n border-bottom: 1px solid #efefef;\n padding-bottom: 2px;\n margin-bottom: 25px; }\n\n.general-module_panel__WjWzy {\n position: relative; }\n\n.general-module_grid__VwNiL {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n width: 100%;\n grid-gap: 8px;\n text-align: center; }\n\n.general-module_obsLabel__9R-PO {\n padding: 10px 20px; }\n .general-module_obsLabel__9R-PO a {\n text-decoration: none;\n color: #336699; }\n .general-module_obsLabel__9R-PO a:hover {\n text-decoration: underline; }\n\n.general-module_loader__gBvfF {\n position: absolute;\n top: 50px;\n left: 0;\n right: 0;\n z-index: 1;\n margin: 0;\n display: flex;\n align-items: center;\n justify-content: center; }\n\n.general-module_loaderBg__URuj7 {\n position: absolute;\n background-color: #cccccc;\n padding: 20px;\n width: 100px;\n border-radius: 10px;\n height: 100%;\n opacity: 0.8; }\n\n.general-module_count__Gnr8W {\n background-color: #efefef;\n color: #666666;\n padding: 2px 8px;\n margin-top: 6px;\n display: inline-block;\n border-radius: 3px;\n font-weight: bold;\n font-size: 18px; }\n";
420
- var styles$4 = {"page":"general-module_page__ziNB9","panel":"general-module_panel__WjWzy","grid":"general-module_grid__VwNiL","obsLabel":"general-module_obsLabel__9R-PO","loader":"general-module_loader__gBvfF","loaderBg":"general-module_loaderBg__URuj7","count":"general-module_count__Gnr8W"};
416
+ var css_248z$4 = ".general-module_page__ziNB9 {\n padding: 80px 10px 10px;\n margin: 0 auto;\n max-width: 1100px; }\n .general-module_page__ziNB9 h1 {\n border-bottom: 1px solid #efefef;\n padding-bottom: 2px;\n margin-bottom: 25px; }\n\n.general-module_panel__WjWzy {\n position: relative; }\n\n.general-module_grid__VwNiL {\n display: grid;\n width: 100%;\n grid-gap: 8px;\n text-align: center; }\n\n.general-module_obsLabel__9R-PO {\n padding: 10px 20px; }\n .general-module_obsLabel__9R-PO a {\n text-decoration: none;\n color: #336699; }\n .general-module_obsLabel__9R-PO a:hover {\n text-decoration: underline; }\n\n.general-module_loader__gBvfF {\n position: absolute;\n top: 50px;\n left: 0;\n right: 0;\n z-index: 1;\n margin: 0;\n display: flex;\n align-items: center;\n justify-content: center; }\n\n.general-module_loaderBg__URuj7 {\n position: absolute;\n background-color: #cccccc;\n padding: 20px;\n width: 100px;\n border-radius: 10px;\n height: 100%;\n opacity: 0.8; }\n\n.general-module_count__Gnr8W {\n background-color: #efefef;\n color: #666666;\n padding: 2px 8px;\n margin-top: 6px;\n display: inline-block;\n border-radius: 3px;\n font-weight: bold;\n font-size: 18px; }\n\n.general-module_tabDesc__zOFiV {\n font-size: 14px;\n margin-bottom: 20px; }\n";
417
+ var styles$4 = {"page":"general-module_page__ziNB9","panel":"general-module_panel__WjWzy","grid":"general-module_grid__VwNiL","obsLabel":"general-module_obsLabel__9R-PO","loader":"general-module_loader__gBvfF","loaderBg":"general-module_loaderBg__URuj7","count":"general-module_count__Gnr8W","tabDesc":"general-module_tabDesc__zOFiV"};
421
418
  styleInject(css_248z$4);
422
419
 
423
420
  var Loader = function (_a) {
@@ -436,11 +433,14 @@ var NoResults = function () {
436
433
  return (jsx("p", { children: "No results found." }));
437
434
  };
438
435
 
439
- var RecentObservationLabel = function (obs) { return (jsxs("div", __assign({ className: styles$4.obsLabel }, { children: [jsx("h3", { children: obs.taxonCommonName || obs.taxonName }), jsx("div", { children: formatDate(obs.obsDate) }), jsx("div", { children: jsx("a", __assign({ href: "".concat(BASE_URL, "/people/").concat(obs.observerUsername), target: "_blank", rel: "noreferrer", onClick: function (e) { return e.stopPropagation(); } }, { children: obs.observerUsername })) })] }))); };
436
+ var RecentObservationLabel = function (obs) {
437
+ var _a, _b, _c;
438
+ return (jsxs("div", __assign({ className: styles$4.obsLabel }, { children: [jsx("h3", __assign({ className: (_a = obs.classes) === null || _a === void 0 ? void 0 : _a.observationLabelTitle }, { children: obs.taxonCommonName || obs.taxonName })), jsx("div", __assign({ className: (_b = obs.classes) === null || _b === void 0 ? void 0 : _b.observationLabelDate }, { children: formatDate(obs.obsDate) })), jsx("div", __assign({ className: (_c = obs.classes) === null || _c === void 0 ? void 0 : _c.observationLabelName }, { children: jsx("a", __assign({ href: "".concat(BASE_URL, "/people/").concat(obs.observerUsername), target: "_blank", rel: "noreferrer", onClick: function (e) { return e.stopPropagation(); } }, { children: obs.observerUsername })) }))] })));
439
+ };
440
440
  var RecentObservations = function (_a) {
441
- var taxonId = _a.taxonId, placeId = _a.placeId, data = _a.data, dataUrl = _a.dataUrl, _b = _a.source, source = _b === void 0 ? DataSource.autoLoad : _b, _c = _a.perPage, perPage = _c === void 0 ? PER_PAGE : _c, components = _a.components, className = _a.className;
442
- var _d = useState(false), loading = _d[0], setLoading = _d[1];
443
- var _e = useState(function () { return (source === DataSource.dataProp) ? data : []; }), observations = _e[0], setObservations = _e[1];
441
+ var taxonId = _a.taxonId, placeId = _a.placeId, data = _a.data, dataUrl = _a.dataUrl, _b = _a.source, source = _b === void 0 ? DataSource.autoLoad : _b, _c = _a.perPage, perPage = _c === void 0 ? PER_PAGE : _c, _d = _a.itemWidth, itemWidth = _d === void 0 ? DEFAULT_ITEM_WIDTH : _d, components = _a.components, className = _a.className, classes = _a.classes, tabDesc = _a.tabDesc;
442
+ var _e = useState(false), loading = _e[0], setLoading = _e[1];
443
+ var _f = useState(function () { return (source === DataSource.dataProp) ? data : []; }), observations = _f[0], setObservations = _f[1];
444
444
  useEffect(function () {
445
445
  if (source !== DataSource.autoLoad) {
446
446
  return;
@@ -498,11 +498,15 @@ var RecentObservations = function (_a) {
498
498
  }, [source, dataUrl]);
499
499
  var Load = (components === null || components === void 0 ? void 0 : components.loader) ? components.loader : Loader;
500
500
  var Label = (components === null || components === void 0 ? void 0 : components.label) ? components.label : RecentObservationLabel;
501
- var classes = styles$4.panel;
501
+ var componentClasses = styles$4.panel;
502
502
  if (className) {
503
- classes += " ".concat(className);
503
+ componentClasses += " ".concat(className);
504
+ }
505
+ var descClasses = styles$4.tabDesc;
506
+ if (classes === null || classes === void 0 ? void 0 : classes.tabDesc) {
507
+ descClasses += " ".concat(classes.tabDesc);
504
508
  }
505
- return (jsxs("div", __assign({ className: classes }, { children: [jsx(Load, { loading: loading }), jsxs("div", __assign({ className: styles$4.grid }, { children: [!loading && observations.length === 0 && jsx(NoResults, {}), observations.map(function (obs) { return (jsx(Observation, __assign({ imageUrl: obs.imageUrl.replace(/square/, "medium"), linkUrl: obs.obsUrl }, { children: jsx(Label, __assign({}, obs)) }), obs.id)); })] }))] })));
509
+ return (jsxs("div", __assign({ className: componentClasses }, { children: [tabDesc && jsx("p", __assign({ className: descClasses }, { children: tabDesc })), jsx(Load, { loading: loading }), jsxs("div", __assign({ className: styles$4.grid, style: { gridTemplateColumns: "repeat(auto-fit, minmax(".concat(itemWidth, "px, 1fr))") } }, { children: [!loading && observations.length === 0 && jsx(NoResults, {}), observations.map(function (obs) { return (jsx(Observation, __assign({ imageUrl: obs.imageUrl.replace(/square/, "medium"), itemWidth: itemWidth, linkUrl: obs.obsUrl }, { children: jsx(Label, __assign({}, obs, { classes: classes })) }), obs.id)); })] }))] })));
506
510
  };
507
511
 
508
512
  var css_248z$3 = "/*\n * Replace this with your own classes\n *\n * e.g.\n * .container {\n * }\n*/\n";
@@ -513,11 +517,14 @@ var Error = function (props) {
513
517
  return (jsx("div", __assign({ className: styles$3['container'] }, { children: jsx("h1", { children: "Welcome to Error!" }) })));
514
518
  };
515
519
 
516
- var CommonTaxaLabel = function (data) { return (jsxs("div", __assign({ className: styles$4.obsLabel }, { children: [jsx("h3", { children: data.taxonCommonName || data.taxonName }), jsx("label", __assign({ className: styles$4.count }, { children: numberWithCommas(data.obsCount) }))] }))); };
520
+ var CommonTaxaLabel = function (data) {
521
+ var _a;
522
+ return (jsxs("div", __assign({ className: styles$4.obsLabel }, { children: [jsx("h3", __assign({ className: (_a = data.classes) === null || _a === void 0 ? void 0 : _a.observationLabelTitle }, { children: data.taxonCommonName || data.taxonName })), jsx("label", __assign({ className: styles$4.count }, { children: numberWithCommas(data.obsCount) }))] })));
523
+ };
517
524
  var CommonTaxa = function (_a) {
518
- var year = _a.year, source = _a.source, taxonId = _a.taxonId, placeId = _a.placeId, _b = _a.perPage, perPage = _b === void 0 ? PER_PAGE : _b, data = _a.data, dataUrl = _a.dataUrl, components = _a.components, className = _a.className;
519
- var _c = useState(function () { return (source === DataSource.dataProp) ? data : []; }), taxa = _c[0], setTaxa = _c[1];
520
- var _d = useState(false), loading = _d[0], setLoading = _d[1];
525
+ var year = _a.year, source = _a.source, taxonId = _a.taxonId, placeId = _a.placeId, _b = _a.perPage, perPage = _b === void 0 ? PER_PAGE : _b, _c = _a.itemWidth, itemWidth = _c === void 0 ? DEFAULT_ITEM_WIDTH : _c, data = _a.data, dataUrl = _a.dataUrl, components = _a.components, classes = _a.classes, className = _a.className, tabDesc = _a.tabDesc;
526
+ var _d = useState(function () { return (source === DataSource.dataProp) ? data : []; }), taxa = _d[0], setTaxa = _d[1];
527
+ var _e = useState(false), loading = _e[0], setLoading = _e[1];
521
528
  useEffect(function () {
522
529
  if (source !== DataSource.autoLoad) {
523
530
  return;
@@ -576,18 +583,25 @@ var CommonTaxa = function (_a) {
576
583
  var Load = (components === null || components === void 0 ? void 0 : components.loader) ? components.loader : Loader;
577
584
  var Label = (components === null || components === void 0 ? void 0 : components.label) ? components.label : CommonTaxaLabel;
578
585
  (components === null || components === void 0 ? void 0 : components.error) ? components.error : Error;
579
- var classes = styles$4.panel;
586
+ var elClasses = styles$4.panel;
580
587
  if (className) {
581
- classes += " ".concat(className);
588
+ elClasses += " ".concat(className);
589
+ }
590
+ var descClasses = styles$4.tabDesc;
591
+ if (elClasses === null || elClasses === void 0 ? void 0 : elClasses.tabDesc) {
592
+ descClasses += " ".concat(elClasses.tabDesc);
582
593
  }
583
- return (jsxs("div", __assign({ className: classes }, { children: [jsx(Load, { loading: loading }), !loading && taxa.length === 0 && jsx(NoResults, {}), jsx("div", __assign({ className: styles$4.grid }, { children: taxa.map(function (data) { return (jsx(Observation, __assign({ imageUrl: data.imageUrl.replace(/square/, "medium"), linkUrl: "".concat(BASE_URL, "/").concat(data.id) }, { children: jsx(Label, __assign({}, data)) }), data.id)); }) }))] })));
594
+ return (jsxs("div", __assign({ className: elClasses }, { children: [tabDesc && jsx("p", __assign({ className: descClasses }, { children: tabDesc })), jsx(Load, { loading: loading }), !loading && taxa.length === 0 && jsx(NoResults, {}), jsx("div", __assign({ className: styles$4.grid, style: { gridTemplateColumns: "repeat(auto-fit, minmax(".concat(itemWidth, "px, 1fr))") } }, { children: taxa.map(function (data) { return (jsx(Observation, __assign({ imageUrl: data.imageUrl.replace(/square/, "medium"), itemWidth: itemWidth, linkUrl: "".concat(BASE_URL, "/").concat(data.id) }, { children: jsx(Label, __assign({}, data, { classes: classes })) }), data.id)); }) }))] })));
584
595
  };
585
596
 
586
- var FavouritesLabel = function (data) { return (jsxs("div", __assign({ className: styles$4.obsLabel }, { children: [jsx("h3", { children: data.taxonCommonName || data.taxonName }), jsx("div", { children: data.observerUsername }), jsx("div", { children: formatDate(data.obsDate) }), jsx("label", __assign({ className: styles$4.count }, { children: data.numFaves }))] }))); };
597
+ var FavouritesLabel = function (data) {
598
+ var _a, _b, _c;
599
+ return (jsxs("div", __assign({ className: styles$4.obsLabel }, { children: [jsx("h3", __assign({ className: (_a = data.classes) === null || _a === void 0 ? void 0 : _a.observationLabelTitle }, { children: data.taxonCommonName || data.taxonName })), jsx("div", __assign({ className: (_b = data.classes) === null || _b === void 0 ? void 0 : _b.observationLabelTitle }, { children: data.observerUsername })), jsx("div", __assign({ className: (_c = data.classes) === null || _c === void 0 ? void 0 : _c.observationLabelDate }, { children: formatDate(data.obsDate) })), jsx("label", __assign({ className: styles$4.count }, { children: data.numFaves }))] })));
600
+ };
587
601
  var Favourites = function (_a) {
588
- var year = _a.year, source = _a.source, taxonId = _a.taxonId, placeId = _a.placeId, data = _a.data, dataUrl = _a.dataUrl, components = _a.components, className = _a.className, _b = _a.perPage, perPage = _b === void 0 ? PER_PAGE : _b;
589
- var _c = useState(function () { return (source === DataSource.dataProp) ? data : []; }), observations = _c[0], setObservations = _c[1];
590
- var _d = useState(false), loading = _d[0], setLoading = _d[1];
602
+ var year = _a.year, source = _a.source, taxonId = _a.taxonId, placeId = _a.placeId, data = _a.data, dataUrl = _a.dataUrl, components = _a.components, className = _a.className, tabDesc = _a.tabDesc, classes = _a.classes, _b = _a.itemWidth, itemWidth = _b === void 0 ? DEFAULT_ITEM_WIDTH : _b, _c = _a.perPage, perPage = _c === void 0 ? PER_PAGE : _c;
603
+ var _d = useState(function () { return (source === DataSource.dataProp) ? data : []; }), observations = _d[0], setObservations = _d[1];
604
+ var _e = useState(false), loading = _e[0], setLoading = _e[1];
591
605
  useEffect(function () {
592
606
  if (source !== DataSource.autoLoad) {
593
607
  return;
@@ -645,11 +659,15 @@ var Favourites = function (_a) {
645
659
  }, [source, dataUrl]);
646
660
  var Load = (components === null || components === void 0 ? void 0 : components.loader) ? components.loader : Loader;
647
661
  var Label = (components === null || components === void 0 ? void 0 : components.label) ? components.label : FavouritesLabel;
648
- var classes = styles$4.panel;
662
+ var elClasses = styles$4.panel;
649
663
  if (className) {
650
- classes += " ".concat(className);
664
+ elClasses += " ".concat(className);
651
665
  }
652
- return (jsxs("div", __assign({ className: classes }, { children: [jsx(Load, { loading: loading }), !loading && observations.length === 0 && jsx(NoResults, {}), jsx("div", __assign({ className: styles$4.grid }, { children: observations.map(function (obs) { return (jsx(Observation, __assign({ imageUrl: obs.imageUrl.replace(/square/, "medium"), linkUrl: obs.obsUrl }, { children: jsx(Label, __assign({}, obs)) }), obs.id)); }) }))] })));
666
+ var descClasses = styles$4.tabDesc;
667
+ if (classes === null || classes === void 0 ? void 0 : classes.tabDesc) {
668
+ descClasses += " ".concat(classes.tabDesc);
669
+ }
670
+ return (jsxs("div", __assign({ className: elClasses }, { children: [tabDesc && jsx("p", __assign({ className: descClasses }, { children: tabDesc })), jsx(Load, { loading: loading }), !loading && observations.length === 0 && jsx(NoResults, {}), jsx("div", __assign({ className: styles$4.grid, style: { gridTemplateColumns: "repeat(auto-fit, minmax(".concat(itemWidth, "px, 1fr))") } }, { children: observations.map(function (obs) { return (jsx(Observation, __assign({ imageUrl: obs.imageUrl.replace(/square/, "medium"), itemWidth: itemWidth, linkUrl: obs.obsUrl }, { children: jsx(Label, __assign({}, obs, { classes: classes })) }), obs.id)); }) }))] })));
653
671
  };
654
672
 
655
673
  var css_248z$2 = ".summary-module_summaryPage__NMmsj {\n position: relative; }\n .summary-module_summaryPage__NMmsj section {\n display: flex; }\n\n.summary-module_top__TKW0j {\n list-style-type: none;\n margin: 0;\n padding: 0; }\n .summary-module_top__TKW0j .summary-module_avatar__16ZeD, .summary-module_top__TKW0j .summary-module_noAvatar__OsMWn {\n margin-right: 10px;\n border-radius: 20px;\n width: 40px;\n height: 40px; }\n .summary-module_top__TKW0j .summary-module_noAvatar__OsMWn {\n display: inline-block;\n background-color: #efefef; }\n .summary-module_top__TKW0j h3 {\n flex: 1;\n color: black; }\n .summary-module_top__TKW0j label {\n flex: 0;\n background-color: #efefef;\n color: #666666;\n padding: 2px 8px;\n margin-top: 6px;\n border-radius: 3px;\n font-weight: bold;\n font-size: 16px; }\n .summary-module_top__TKW0j li {\n border-radius: 3px; }\n .summary-module_top__TKW0j li a {\n display: flex;\n align-items: center;\n text-decoration: none; }\n .summary-module_top__TKW0j li:hover {\n background-color: #efefef; }\n\n.summary-module_countSummary__Xc2Kd {\n list-style-type: none;\n line-height: 28px;\n font-size: 16px;\n padding: 0;\n margin: 0 0 30px; }\n\n.summary-module_seasonalityBlock__BL8L- {\n flex: 3; }\n\n.summary-module_observersBlock__0wnbm {\n flex: 1;\n margin-left: 40px;\n margin-bottom: -40px; }\n\n@media screen and (max-width: 800px) {\n .summary-module_summaryPage__NMmsj section {\n display: block; }\n .summary-module_seasonalityBlock__BL8L- {\n height: 250px;\n margin-bottom: 80px; }\n .summary-module_observersBlock__0wnbm {\n margin-left: 0; } }\n";
@@ -657,8 +675,12 @@ var styles$2 = {"summaryPage":"summary-module_summaryPage__NMmsj","top":"summary
657
675
  styleInject(css_248z$2);
658
676
 
659
677
  var ObserverList = function (_a) {
660
- var observers = _a.observers;
661
- return (jsx("ul", __assign({ className: styles$2.top }, { children: observers.map(function (obs) { return (jsx("li", { children: jsxs("a", __assign({ href: "".concat(BASE_URL, "/people/").concat(obs.userName), target: "_blank", rel: "noreferrer" }, { children: [obs.iconUrl ? jsx("img", { src: obs.iconUrl || "", className: styles$2.avatar, alt: "User icon" }) :
678
+ var observers = _a.observers, _b = _a.className, className = _b === void 0 ? '' : _b;
679
+ var classes = styles$2.top;
680
+ if (className) {
681
+ classes += " ".concat(className);
682
+ }
683
+ return (jsx("ul", __assign({ className: classes }, { children: observers.map(function (obs) { return (jsx("li", { children: jsxs("a", __assign({ href: "".concat(BASE_URL, "/people/").concat(obs.userName), target: "_blank", rel: "noreferrer" }, { children: [obs.iconUrl ? jsx("img", { src: obs.iconUrl || "", className: styles$2.avatar, alt: "User icon" }) :
662
684
  jsx("div", { className: styles$2.noAvatar }), jsx("h3", { children: obs.userName }), jsx("label", { children: numberWithCommas(obs.numObservations) })] })) }, obs.id)); }) })));
663
685
  };
664
686
 
@@ -691,7 +713,7 @@ var SeasonalityGraph = function (_a) {
691
713
 
692
714
  var Summary = function (_a) {
693
715
  var _b, _c, _d;
694
- var source = _a.source; _a.data; var dataUrl = _a.dataUrl, taxonId = _a.taxonId, placeId = _a.placeId, year = _a.year;
716
+ var source = _a.source; _a.data; var dataUrl = _a.dataUrl, taxonId = _a.taxonId, placeId = _a.placeId, year = _a.year, classes = _a.classes, tabDesc = _a.tabDesc;
695
717
  var _e = useState({}), summaryData = _e[0], setSummaryData = _e[1];
696
718
  var _f = useState(false), loading = _f[0], setLoading = _f[1];
697
719
  useEffect(function () {
@@ -749,7 +771,15 @@ var Summary = function (_a) {
749
771
  });
750
772
  }); })();
751
773
  }, [source, dataUrl]);
752
- return (jsxs("div", __assign({ className: styles$2.summaryPage }, { children: [jsx(Loader, { loading: loading }), jsxs("ul", __assign({ className: styles$2.countSummary }, { children: [jsxs("li", { children: ["Total number of observers: ", jsx("b", { children: ((_b = summaryData.observers) === null || _b === void 0 ? void 0 : _b.totalCount) ? numberWithCommas(summaryData.observers.totalCount) : "" })] }), jsxs("li", { children: ["Total number of observations: ", jsx("b", { children: ((_c = summaryData.observations) === null || _c === void 0 ? void 0 : _c.totalCount) ? numberWithCommas(summaryData.observations.totalCount) : "" })] })] })), jsxs("section", { children: [jsxs("div", __assign({ className: styles$2.seasonalityBlock }, { children: [jsx("h1", { children: "Seasonality" }), summaryData.seasonalityData && jsx(SeasonalityGraph, { data: summaryData.seasonalityData.monthOfYear })] })), jsxs("div", __assign({ className: styles$2.observersBlock }, { children: [jsx("h1", { children: "Top observers" }), ((_d = summaryData.observers) === null || _d === void 0 ? void 0 : _d.top) && jsx(ObserverList, { observers: summaryData.observers.top })] }))] })] })));
774
+ var countSummaryClasses = styles$2.countSummary;
775
+ if (classes === null || classes === void 0 ? void 0 : classes.statsCountSummary) {
776
+ countSummaryClasses += " ".concat(classes.statsCountSummary);
777
+ }
778
+ var descClasses = styles$2.tabDesc;
779
+ if (classes === null || classes === void 0 ? void 0 : classes.tabDesc) {
780
+ descClasses += " ".concat(classes.tabDesc);
781
+ }
782
+ return (jsxs("div", __assign({ className: styles$2.summaryPage }, { children: [tabDesc && jsx("p", __assign({ className: descClasses }, { children: tabDesc })), jsx(Loader, { loading: loading }), jsxs("ul", __assign({ className: countSummaryClasses }, { children: [jsxs("li", { children: ["Total number of observers: ", jsx("b", { children: ((_b = summaryData.observers) === null || _b === void 0 ? void 0 : _b.totalCount) ? numberWithCommas(summaryData.observers.totalCount) : "" })] }), jsxs("li", { children: ["Total number of observations: ", jsx("b", { children: ((_c = summaryData.observations) === null || _c === void 0 ? void 0 : _c.totalCount) ? numberWithCommas(summaryData.observations.totalCount) : "" })] })] })), jsxs("section", { children: [jsxs("div", __assign({ className: styles$2.seasonalityBlock }, { children: [jsx("h1", { children: "Seasonality" }), summaryData.seasonalityData && jsx(SeasonalityGraph, { data: summaryData.seasonalityData.monthOfYear })] })), jsxs("div", __assign({ className: styles$2.observersBlock }, { children: [jsx("h1", { children: "Top observers" }), ((_d = summaryData.observers) === null || _d === void 0 ? void 0 : _d.top) && jsx(ObserverList, { observers: summaryData.observers.top, className: classes === null || classes === void 0 ? void 0 : classes.observersList })] }))] })] })));
753
783
  };
754
784
 
755
785
  var css_248z$1 = ".year-dropdown-module_dropdown__iD9sj {\n font-size: 18px; }\n";
@@ -800,26 +830,34 @@ var useFeatureTitles = function (features) {
800
830
  };
801
831
 
802
832
  var Tabs = function (_a) {
803
- var selectedTab = _a.selectedTab, onChangeTab = _a.onChangeTab, features = _a.features;
833
+ var selectedTab = _a.selectedTab, onChangeTab = _a.onChangeTab, features = _a.features, className = _a.className;
804
834
  var titles = useFeatureTitles(features);
805
- return (jsxs("ul", __assign({ className: styles.tabs }, { children: [features.recentObservations && (jsx("li", __assign({ onClick: function () { return onChangeTab(Tab.recent); }, className: selectedTab === Tab.recent ? styles.selected : "" }, { children: titles[Tab.recent] }))), features.commonTaxa && (jsx("li", __assign({ onClick: function () { return onChangeTab(Tab.commonTaxa); }, className: selectedTab === Tab.commonTaxa ? styles.selected : "" }, { children: titles[Tab.commonTaxa] }))), features.favourites && (jsx("li", __assign({ onClick: function () { return onChangeTab(Tab.favourites); }, className: selectedTab === Tab.favourites ? styles.selected : "" }, { children: titles[Tab.favourites] }))), features.stats && (jsx("li", __assign({ onClick: function () { return onChangeTab(Tab.stats); }, className: selectedTab === Tab.stats ? styles.selected : "" }, { children: titles[Tab.stats] })))] })));
835
+ var classes = styles.tabs;
836
+ if (className) {
837
+ classes += " ".concat(className);
838
+ }
839
+ return (jsxs("ul", __assign({ className: classes }, { children: [features.recentObservations && (jsx("li", __assign({ onClick: function () { return onChangeTab(Tab.recent); }, className: selectedTab === Tab.recent ? styles.selected : "" }, { children: titles[Tab.recent] }))), features.commonTaxa && (jsx("li", __assign({ onClick: function () { return onChangeTab(Tab.commonTaxa); }, className: selectedTab === Tab.commonTaxa ? styles.selected : "" }, { children: titles[Tab.commonTaxa] }))), features.favourites && (jsx("li", __assign({ onClick: function () { return onChangeTab(Tab.favourites); }, className: selectedTab === Tab.favourites ? styles.selected : "" }, { children: titles[Tab.favourites] }))), features.stats && (jsx("li", __assign({ onClick: function () { return onChangeTab(Tab.stats); }, className: selectedTab === Tab.stats ? styles.selected : "" }, { children: titles[Tab.stats] })))] })));
806
840
  };
807
841
 
808
842
  var TaxonPanel = function (_a) {
809
- var taxonId = _a.taxonId, placeId = _a.placeId, dataSource = _a.dataSource, config = _a.config, baseURL = _a.baseURL;
843
+ var taxonId = _a.taxonId, placeId = _a.placeId, dataSource = _a.dataSource, config = _a.config, baseUrl = _a.baseUrl, itemWidth = _a.itemWidth, classes = _a.classes, tabDescs = _a.tabDescs;
810
844
  var _b = useState(Tab.recent), tab = _b[0], setTab = _b[1];
811
845
  var _c = useState("all"), year = _c[0], setYear = _c[1];
812
846
  var titles = useFeatureTitles(config.features);
813
- var _d = useState(function () { return config.taxa.find(function (i) { return i.taxonId === taxonId; }); }), taxonInfo = _d[0]; _d[1];
814
- var _e = useState(function () { return config.places.find(function (i) { return i.placeId === placeId; }); }), placeInfo = _e[0]; _e[1];
847
+ var taxonInfo = useState(function () { return config.taxa.find(function (i) { return i.taxonId === taxonId; }); })[0];
848
+ var placeInfo = useState(function () { return config.places.find(function (i) { return i.placeId === placeId; }); })[0];
849
+ console.log("...", classes);
815
850
  var getCurrentTab = function () {
816
851
  switch (tab) {
817
852
  case Tab.recent: {
818
853
  var props = {
819
- source: dataSource
854
+ source: dataSource,
855
+ itemWidth: itemWidth,
856
+ classes: classes,
857
+ tabDesc: tabDescs === null || tabDescs === void 0 ? void 0 : tabDescs.recentDesc
820
858
  };
821
859
  if (dataSource === DataSource.url) {
822
- props.dataUrl = baseURL + '/' + getSourceFile(Feature.recentObservations, taxonInfo, placeInfo);
860
+ props.dataUrl = baseUrl + '/' + getSourceFile(Feature.recentObservations, taxonInfo, placeInfo);
823
861
  }
824
862
  else {
825
863
  props.placeId = placeId;
@@ -829,11 +867,14 @@ var TaxonPanel = function (_a) {
829
867
  }
830
868
  case Tab.commonTaxa: {
831
869
  var props = {
870
+ classes: classes,
832
871
  source: dataSource,
833
- year: year
872
+ itemWidth: itemWidth,
873
+ year: year,
874
+ tabDesc: tabDescs === null || tabDescs === void 0 ? void 0 : tabDescs.mostCommonDesc
834
875
  };
835
876
  if (dataSource === DataSource.url) {
836
- props.dataUrl = baseURL + '/' + getSourceFile(Feature.commonTaxa, taxonInfo, placeInfo, year);
877
+ props.dataUrl = baseUrl + '/' + getSourceFile(Feature.commonTaxa, taxonInfo, placeInfo, year);
837
878
  }
838
879
  else {
839
880
  props.placeId = placeId;
@@ -843,11 +884,14 @@ var TaxonPanel = function (_a) {
843
884
  }
844
885
  case Tab.favourites: {
845
886
  var props = {
887
+ classes: classes,
888
+ itemWidth: itemWidth,
846
889
  source: dataSource,
847
- year: year
890
+ year: year,
891
+ tabDesc: tabDescs === null || tabDescs === void 0 ? void 0 : tabDescs.mostFavouritedDesc
848
892
  };
849
893
  if (dataSource === DataSource.url) {
850
- props.dataUrl = baseURL + '/' + getSourceFile(Feature.favourites, taxonInfo, placeInfo, year);
894
+ props.dataUrl = baseUrl + '/' + getSourceFile(Feature.favourites, taxonInfo, placeInfo, year);
851
895
  }
852
896
  else {
853
897
  props.placeId = placeId;
@@ -857,11 +901,13 @@ var TaxonPanel = function (_a) {
857
901
  }
858
902
  case Tab.stats: {
859
903
  var props = {
904
+ classes: classes,
860
905
  source: dataSource,
861
- year: year
906
+ year: year,
907
+ tabDesc: tabDescs === null || tabDescs === void 0 ? void 0 : tabDescs.generalStatsDesc
862
908
  };
863
909
  if (dataSource === DataSource.url) {
864
- props.dataUrl = baseURL + '/' + getSourceFile(Feature.stats, taxonInfo, placeInfo, year);
910
+ props.dataUrl = baseUrl + '/' + getSourceFile(Feature.stats, taxonInfo, placeInfo, year);
865
911
  }
866
912
  else {
867
913
  props.placeId = placeId;
@@ -871,7 +917,9 @@ var TaxonPanel = function (_a) {
871
917
  }
872
918
  }
873
919
  };
874
- return (jsxs("div", __assign({ className: styles$4.page }, { children: [jsx(Tabs, { selectedTab: tab, onChangeTab: setTab, features: config.features }), jsxs("div", { children: [tab !== Tab.recent && (jsx("div", __assign({ style: { float: "right" } }, { children: jsx(Years, { value: year, onChange: setYear }) }))), jsx("h1", { children: titles[tab] })] }), getCurrentTab()] })));
920
+ var tabsClass = (classes === null || classes === void 0 ? void 0 : classes.tabs) || '';
921
+ var yearDropdownClass = (classes === null || classes === void 0 ? void 0 : classes.yearsDropdown) || '';
922
+ return (jsxs("div", __assign({ className: styles$4.page }, { children: [jsx(Tabs, { selectedTab: tab, onChangeTab: setTab, features: config.features, className: tabsClass }), jsxs("div", { children: [tab !== Tab.recent && (jsx("div", __assign({ style: { float: "right" }, className: yearDropdownClass }, { children: jsx(Years, { value: year, onChange: setYear }) }))), jsx("h1", __assign({ className: classes === null || classes === void 0 ? void 0 : classes.pageHeadings }, { children: titles[tab] }))] }), getCurrentTab()] })));
875
923
  };
876
924
 
877
925
  export { constants as C, CommonTaxa, CommonTaxaLabel, DataSource, Favourites, FavouritesLabel, Feature, Observation, RecentObservationLabel, RecentObservations, SeasonalityGraph, Summary, Tab, TaxonPanel as default, formatDate, getCommonTaxa, getCurrentYear, getFavourites, getObservationSummary, getObserverSummary, getRecentObservations, getSeasonalityData, getSourceFile, getSummary, numberWithCommas };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@imerss/inat-components",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "main": "dist/index.js",
5
5
  "types": "./dist/index.d.ts",
6
6
  "scripts": {
@@ -30,14 +30,9 @@ export const getFavourites = async ({ year, taxonId, placeId, perPage }: Favouri
30
30
  url += `&d1=${year}-01-01&d2=${year}-12-31`;
31
31
  }
32
32
 
33
- const response = await fetch(url, {
34
- // method: 'GET',
35
- // headers: {
36
- // Accept: 'application/json'
37
- // }
38
- });
39
-
33
+ const response = await fetch(url);
40
34
  const resp = await response.json();
35
+
41
36
  const sortedTaxa = resp.results.filter((i: any) => i.faves.length > 0).sort((a: any, b: any) => {
42
37
  if (a.faves.length > b.faves.length) {
43
38
  return -1;
@@ -51,7 +46,7 @@ export const getFavourites = async ({ year, taxonId, placeId, perPage }: Favouri
51
46
  totalResults: resp.total_results,
52
47
  results: sortedTaxa.map((row: any) => ({
53
48
  id: row.id,
54
- imageUrl: row.taxon?.default_photo?.square_url || "",
49
+ imageUrl: row.observation_photos[0].photo.url || "",
55
50
  obsDate: row.observed_on_string,
56
51
  obsUrl: row.uri,
57
52
  taxonName: row.taxon.name || "",
@@ -2,3 +2,4 @@ export const BASE_API_URL = 'https://api.inaturalist.org';
2
2
  export const PER_PAGE = 100;
3
3
  export const BASE_URL = "https://www.inaturalist.org"; // /people/name_here
4
4
  export const ERROR_LOG_FILENAME = "inat-components-error.log";
5
+ export const DEFAULT_ITEM_WIDTH = 150;
@@ -40,6 +40,18 @@ export enum Tab {
40
40
  stats = "stats"
41
41
  }
42
42
 
43
+ export type BaseClasses = {
44
+ tabs?: string;
45
+ yearsDropdown?: string;
46
+ pageHeadings?: string;
47
+ observationLabelTitle?: string;
48
+ observationLabelDate?: string;
49
+ observationLabelName?: string;
50
+ observersList?: string;
51
+ statsCountSummary?: string;
52
+ tabDesc?: string;
53
+ }
54
+
43
55
  export type BaseComponentProps = {
44
56
  taxonId?: number;
45
57
  placeId?: number;
@@ -49,11 +61,14 @@ export type BaseComponentProps = {
49
61
  data?: any;
50
62
  dataUrl?: string;
51
63
  className?: string;
64
+ itemWidth?: number;
52
65
  components?: {
53
66
  label?: any;
54
67
  error?: any;
55
68
  loader?: any;
56
69
  };
70
+ classes?: BaseClasses;
71
+ tabDesc?: string;
57
72
  };
58
73
 
59
74
  export type TaxaConfig = {
@@ -66,4 +81,11 @@ export type PlaceConfig = {
66
81
  label: string;
67
82
  short: string;
68
83
  placeId: number;
69
- }
84
+ }
85
+
86
+ export type TabDescs = {
87
+ recentDesc?: string;
88
+ mostCommonDesc?: string;
89
+ mostFavouritedDesc?: string;
90
+ generalStatsDesc?: string;
91
+ }
@@ -3,7 +3,11 @@ import {Feature, PlaceConfig, TaxaConfig} from "../typings";
3
3
  /*
4
4
  * Note that right now the source filenames aren't configurable.
5
5
  */
6
- export const getSourceFile = (api: Feature, taxonInfo: TaxaConfig, placeInfo: PlaceConfig, year?: string | number): string => {
6
+ export const getSourceFile = (api: Feature, taxonInfo?: TaxaConfig, placeInfo?: PlaceConfig, year?: string | number): string => {
7
+ if (!taxonInfo || !placeInfo) {
8
+ return '';
9
+ }
10
+
7
11
  const yearStr = year === "all" ? "allyears" : year;
8
12
 
9
13
  let filename = "";
@@ -1,5 +1,5 @@
1
1
  import React, {useEffect, useState} from 'react';
2
- import {numberWithCommas, C, CommonTaxData, getCommonTaxa, BaseComponentProps, DataSource} from "../../__shared";
2
+ import {numberWithCommas, C, CommonTaxData, getCommonTaxa, BaseComponentProps, BaseClasses, DataSource} from "../../__shared";
3
3
  import {Observation} from "../observation/observation";
4
4
  import Loader from "../loader/loader";
5
5
  import Error from "../error/error";
@@ -10,9 +10,9 @@ export type CommonTaxaProps = BaseComponentProps & {
10
10
  year: string | number;
11
11
  }
12
12
 
13
- export const CommonTaxaLabel = (data: CommonTaxData) => (
13
+ export const CommonTaxaLabel = (data: CommonTaxData & { classes: BaseClasses }) => (
14
14
  <div className={styles.obsLabel}>
15
- <h3>{data.taxonCommonName || data.taxonName}</h3>
15
+ <h3 className={data.classes?.observationLabelTitle}>{data.taxonCommonName || data.taxonName}</h3>
16
16
  <label className={styles.count}>{numberWithCommas(data.obsCount)}</label>
17
17
  </div>
18
18
  );
@@ -23,10 +23,13 @@ export const CommonTaxa = ({
23
23
  taxonId,
24
24
  placeId,
25
25
  perPage = C.PER_PAGE,
26
+ itemWidth = C.DEFAULT_ITEM_WIDTH,
26
27
  data,
27
28
  dataUrl,
28
29
  components,
29
- className
30
+ classes,
31
+ className,
32
+ tabDesc
30
33
  }: CommonTaxaProps) => {
31
34
  const [taxa, setTaxa] = useState<any>(() => (source === DataSource.dataProp) ? data : []);
32
35
  const [loading, setLoading] = useState(false);
@@ -75,22 +78,28 @@ export const CommonTaxa = ({
75
78
  const Label = components?.label ? components.label as any : CommonTaxaLabel;
76
79
  const ErrorMsg = components?.error ? components.error as any : Error;
77
80
 
78
- let classes = styles.panel;
81
+ let elClasses = styles.panel;
79
82
  if (className) {
80
- classes += ` ${className}`;
83
+ elClasses += ` ${className}`;
84
+ }
85
+ let descClasses = styles.tabDesc;
86
+ if (elClasses?.tabDesc) {
87
+ descClasses += ` ${elClasses.tabDesc}`;
81
88
  }
82
89
 
83
90
  return (
84
- <div className={classes}>
91
+ <div className={elClasses}>
92
+ {tabDesc && <p className={descClasses}>{tabDesc}</p>}
85
93
  <Load loading={loading}/>
86
94
  {!loading && taxa.length === 0 && <NoResults/>}
87
- <div className={styles.grid}>
95
+ <div className={styles.grid} style={{ gridTemplateColumns: `repeat(auto-fit, minmax(${itemWidth}px, 1fr))` }}>
88
96
  {taxa.map((data: CommonTaxData) => (
89
97
  <Observation
90
98
  key={data.id}
91
99
  imageUrl={data.imageUrl.replace(/square/, "medium")}
100
+ itemWidth={itemWidth}
92
101
  linkUrl={`${C.BASE_URL}/${data.id}`}>
93
- <Label {...data} />
102
+ <Label {...data} classes={classes} />
94
103
  </Observation>
95
104
  ))}
96
105
  </div>
@@ -1,19 +1,20 @@
1
1
  import React, {useEffect, useState} from "react";
2
- import {formatDate, C, getFavourites, FavouritesData, BaseComponentProps, DataSource} from "../../__shared";
2
+ import {formatDate, C, getFavourites, FavouritesData, BaseComponentProps, DataSource, BaseClasses} from "../../__shared";
3
3
  import {Observation} from "../observation/observation";
4
4
  import Loader from "../loader/loader";
5
5
  import {NoResults} from "../no-results/no-results";
6
6
  import generalStyles from "../shared/css/general.module.scss";
7
+ import styles from "../shared/css/general.module.scss";
7
8
 
8
9
  export type FavouritesProps = BaseComponentProps & {
9
10
  year: string | number;
10
11
  }
11
12
 
12
- export const FavouritesLabel = (data: FavouritesData) => (
13
+ export const FavouritesLabel = (data: FavouritesData & { classes: BaseClasses }) => (
13
14
  <div className={generalStyles.obsLabel}>
14
- <h3>{data.taxonCommonName || data.taxonName}</h3>
15
- <div>{data.observerUsername}</div>
16
- <div>{formatDate(data.obsDate)}</div>
15
+ <h3 className={data.classes?.observationLabelTitle}>{data.taxonCommonName || data.taxonName}</h3>
16
+ <div className={data.classes?.observationLabelTitle}>{data.observerUsername}</div>
17
+ <div className={data.classes?.observationLabelDate}>{formatDate(data.obsDate)}</div>
17
18
  <label className={generalStyles.count}>{data.numFaves}</label>
18
19
  </div>
19
20
  );
@@ -27,6 +28,9 @@ export const Favourites = ({
27
28
  dataUrl,
28
29
  components,
29
30
  className,
31
+ tabDesc,
32
+ classes,
33
+ itemWidth = C.DEFAULT_ITEM_WIDTH,
30
34
  perPage = C.PER_PAGE
31
35
  }: FavouritesProps) => {
32
36
  const [observations, setObservations] = useState<any>(() => (source === DataSource.dataProp) ? data : []);
@@ -75,21 +79,27 @@ export const Favourites = ({
75
79
  const Load = components?.loader ? components.loader as any : Loader;
76
80
  const Label = components?.label ? components.label as any : FavouritesLabel;
77
81
 
78
- let classes = generalStyles.panel;
82
+ let elClasses = generalStyles.panel;
79
83
  if (className) {
80
- classes += ` ${className}`;
84
+ elClasses += ` ${className}`;
85
+ }
86
+ let descClasses = styles.tabDesc;
87
+ if (classes?.tabDesc) {
88
+ descClasses += ` ${classes.tabDesc}`;
81
89
  }
82
90
  return (
83
- <div className={classes}>
91
+ <div className={elClasses}>
92
+ {tabDesc && <p className={descClasses}>{tabDesc}</p>}
84
93
  <Load loading={loading}/>
85
94
  {!loading && observations.length === 0 && <NoResults/>}
86
- <div className={generalStyles.grid}>
95
+ <div className={generalStyles.grid} style={{ gridTemplateColumns: `repeat(auto-fit, minmax(${itemWidth}px, 1fr))` }}>
87
96
  {observations.map((obs: any) => (
88
97
  <Observation
89
98
  key={obs.id}
90
99
  imageUrl={obs.imageUrl.replace(/square/, "medium")}
100
+ itemWidth={itemWidth}
91
101
  linkUrl={obs.obsUrl}>
92
- <Label {...obs} />
102
+ <Label {...obs} classes={classes} />
93
103
  </Observation>
94
104
  ))}
95
105
  </div>
@@ -4,10 +4,11 @@ import styles from "./observation.module.scss";
4
4
  export type ObservationProps = {
5
5
  imageUrl: string;
6
6
  linkUrl: string;
7
+ itemWidth: number;
7
8
  children: any;
8
9
  };
9
10
 
10
- export const Observation = ({imageUrl, linkUrl, children}: ObservationProps) => {
11
+ export const Observation = ({imageUrl, linkUrl, itemWidth, children}: ObservationProps) => {
11
12
  const gotoObservation = useCallback(() => {
12
13
  window.open(linkUrl);
13
14
  }, [linkUrl]);
@@ -15,8 +16,8 @@ export const Observation = ({imageUrl, linkUrl, children}: ObservationProps) =>
15
16
  return (
16
17
  <article className={styles.obs} onClick={gotoObservation}>
17
18
  <div style={{
18
- width: 200,
19
- height: 200,
19
+ width: itemWidth,
20
+ height: itemWidth,
20
21
  overflow: "hidden",
21
22
  borderRadius: 5,
22
23
  background: `url(${imageUrl}) 50% 50% no-repeat`,
@@ -1,5 +1,13 @@
1
1
  import React, {useEffect, useState} from "react";
2
- import {formatDate, C, getRecentObservations, RecentObservationData, BaseComponentProps, DataSource} from "../../__shared";
2
+ import {
3
+ formatDate,
4
+ C,
5
+ getRecentObservations,
6
+ RecentObservationData,
7
+ BaseComponentProps,
8
+ DataSource,
9
+ BaseClasses
10
+ } from "../../__shared";
3
11
  import {Observation} from "../observation/observation";
4
12
  import Loader from "../loader/loader";
5
13
  import {NoResults} from "../no-results/no-results";
@@ -7,11 +15,11 @@ import styles from "../shared/css/general.module.scss";
7
15
 
8
16
  export type RecentObservationsProps = BaseComponentProps;
9
17
 
10
- export const RecentObservationLabel = (obs: RecentObservationData) => (
18
+ export const RecentObservationLabel = (obs: RecentObservationData & { classes: BaseClasses }) => (
11
19
  <div className={styles.obsLabel}>
12
- <h3>{obs.taxonCommonName || obs.taxonName}</h3>
13
- <div>{formatDate(obs.obsDate)}</div>
14
- <div>
20
+ <h3 className={obs.classes?.observationLabelTitle}>{obs.taxonCommonName || obs.taxonName}</h3>
21
+ <div className={obs.classes?.observationLabelDate}>{formatDate(obs.obsDate)}</div>
22
+ <div className={obs.classes?.observationLabelName}>
15
23
  <a href={`${C.BASE_URL}/people/${obs.observerUsername}`}
16
24
  target="_blank"
17
25
  rel="noreferrer"
@@ -29,8 +37,11 @@ export const RecentObservations = ({
29
37
  dataUrl,
30
38
  source = DataSource.autoLoad,
31
39
  perPage = C.PER_PAGE,
40
+ itemWidth = C.DEFAULT_ITEM_WIDTH,
32
41
  components,
33
- className
42
+ className,
43
+ classes,
44
+ tabDesc
34
45
  }: RecentObservationsProps) => {
35
46
  const [loading, setLoading] = useState(false);
36
47
  const [observations, setObservations] = useState<any>(() => (source === DataSource.dataProp) ? data : []);
@@ -78,21 +89,27 @@ export const RecentObservations = ({
78
89
  const Load = components?.loader ? components.loader as any : Loader;
79
90
  const Label = components?.label ? components.label as any : RecentObservationLabel;
80
91
 
81
- let classes = styles.panel;
92
+ let componentClasses = styles.panel;
82
93
  if (className) {
83
- classes += ` ${className}`;
94
+ componentClasses += ` ${className}`;
95
+ }
96
+ let descClasses = styles.tabDesc;
97
+ if (classes?.tabDesc) {
98
+ descClasses += ` ${classes.tabDesc}`;
84
99
  }
85
100
  return (
86
- <div className={classes}>
101
+ <div className={componentClasses}>
102
+ {tabDesc && <p className={descClasses}>{tabDesc}</p>}
87
103
  <Load loading={loading}/>
88
- <div className={styles.grid}>
104
+ <div className={styles.grid} style={{ gridTemplateColumns: `repeat(auto-fit, minmax(${itemWidth}px, 1fr))` }}>
89
105
  {!loading && observations.length === 0 && <NoResults/>}
90
106
  {observations.map((obs: RecentObservationData) => (
91
107
  <Observation
92
108
  key={obs.id}
93
109
  imageUrl={obs.imageUrl.replace(/square/, "medium")}
110
+ itemWidth={itemWidth}
94
111
  linkUrl={obs.obsUrl}>
95
- <Label {...obs} />
112
+ <Label {...obs} classes={classes} />
96
113
  </Observation>
97
114
  ))}
98
115
  </div>
@@ -16,7 +16,6 @@
16
16
 
17
17
  .grid {
18
18
  display: grid;
19
- grid-template-columns: repeat(auto-fit, minmax(200px, 1fr) );
20
19
  width: 100%;
21
20
  grid-gap: 8px;
22
21
  text-align: center;
@@ -67,3 +66,8 @@
67
66
  font-weight: bold;
68
67
  font-size: 18px;
69
68
  }
69
+
70
+ .tabDesc {
71
+ font-size: 14px;
72
+ margin-bottom: 20px;
73
+ }
@@ -2,17 +2,23 @@ import React from "react";
2
2
  import {numberWithCommas, Observer, C} from "../../__shared";
3
3
  import styles from "./summary.module.scss";
4
4
 
5
- export const ObserverList = ({observers}: { observers: Observer[] }) => (
6
- <ul className={styles.top}>
7
- {observers.map((obs) => (
8
- <li key={obs.id}>
9
- <a href={`${C.BASE_URL}/people/${obs.userName}`} target="_blank" rel="noreferrer">
10
- {obs.iconUrl ? <img src={obs.iconUrl || ""} className={styles.avatar} alt="User icon"/> :
11
- <div className={styles.noAvatar}/>}
12
- <h3>{obs.userName}</h3>
13
- <label>{numberWithCommas(obs.numObservations)}</label>
14
- </a>
15
- </li>
16
- ))}
17
- </ul>
18
- );
5
+ export const ObserverList = ({observers, className = ''}: { observers: Observer[], className?: string }) => {
6
+ let classes = styles.top;
7
+ if (className) {
8
+ classes += ` ${className}`;
9
+ }
10
+ return (
11
+ <ul className={classes}>
12
+ {observers.map((obs) => (
13
+ <li key={obs.id}>
14
+ <a href={`${C.BASE_URL}/people/${obs.userName}`} target="_blank" rel="noreferrer">
15
+ {obs.iconUrl ? <img src={obs.iconUrl || ""} className={styles.avatar} alt="User icon"/> :
16
+ <div className={styles.noAvatar}/>}
17
+ <h3>{obs.userName}</h3>
18
+ <label>{numberWithCommas(obs.numObservations)}</label>
19
+ </a>
20
+ </li>
21
+ ))}
22
+ </ul>
23
+ );
24
+ };
@@ -1,5 +1,5 @@
1
1
  import React, {useEffect, useState} from "react";
2
- import {numberWithCommas, DataSource, getSummary, FavouritesRespData} from "../../__shared";
2
+ import {BaseClasses, numberWithCommas, DataSource, getSummary, FavouritesRespData} from "../../__shared";
3
3
  import { ObserverList } from './ObserverList';
4
4
  import Loader from "../loader/loader";
5
5
  import {SeasonalityGraph} from "../seasonality-graph/seasonality-graph";
@@ -10,11 +10,13 @@ export type SummaryProps = {
10
10
  taxonId: number;
11
11
  placeId: number;
12
12
  year: string | number;
13
- data?: FavouritesRespData; /// TODO
13
+ data?: FavouritesRespData;
14
14
  dataUrl?: string;
15
+ classes?: BaseClasses;
16
+ tabDesc?: string;
15
17
  };
16
18
 
17
- export const Summary = ({ source, data, dataUrl, taxonId, placeId, year }: SummaryProps) => {
19
+ export const Summary = ({ source, data, dataUrl, taxonId, placeId, year, classes, tabDesc }: SummaryProps) => {
18
20
  const [summaryData, setSummaryData] = useState<any>({});
19
21
  const [loading, setLoading] = useState(false);
20
22
 
@@ -58,24 +60,31 @@ export const Summary = ({ source, data, dataUrl, taxonId, placeId, year }: Summa
58
60
  })();
59
61
  }, [source, dataUrl]);
60
62
 
63
+ let countSummaryClasses = styles.countSummary;
64
+ if (classes?.statsCountSummary) {
65
+ countSummaryClasses += ` ${classes.statsCountSummary}`;
66
+ }
67
+ let descClasses = styles.tabDesc;
68
+ if (classes?.tabDesc) {
69
+ descClasses += ` ${classes.tabDesc}`;
70
+ }
71
+
61
72
  return (
62
73
  <div className={styles.summaryPage}>
74
+ {tabDesc && <p className={descClasses}>{tabDesc}</p>}
63
75
  <Loader loading={loading} />
64
-
65
- <ul className={styles.countSummary}>
76
+ <ul className={countSummaryClasses}>
66
77
  <li>Total number of observers: <b>{summaryData.observers?.totalCount ? numberWithCommas(summaryData.observers.totalCount): ""}</b></li>
67
78
  <li>Total number of observations: <b>{summaryData.observations?.totalCount ? numberWithCommas(summaryData.observations.totalCount): ""}</b></li>
68
79
  </ul>
69
-
70
80
  <section>
71
81
  <div className={styles.seasonalityBlock}>
72
82
  <h1>Seasonality</h1>
73
83
  {summaryData.seasonalityData && <SeasonalityGraph data={summaryData.seasonalityData.monthOfYear} />}
74
84
  </div>
75
-
76
85
  <div className={styles.observersBlock}>
77
86
  <h1>Top observers</h1>
78
- {summaryData.observers?.top && <ObserverList observers={summaryData.observers.top} />}
87
+ {summaryData.observers?.top && <ObserverList observers={summaryData.observers.top} className={classes?.observersList} />}
79
88
  </div>
80
89
  </section>
81
90
  </div>
@@ -6,14 +6,20 @@ import {useFeatureTitles} from "../hooks/hooks";
6
6
  export type TabsProps = {
7
7
  selectedTab: Tab;
8
8
  onChangeTab: (tab: Tab) => void;
9
- features: any; // TODO
9
+ features: any;
10
+ className?: string;
10
11
  }
11
12
 
12
- const Tabs = ({ selectedTab, onChangeTab, features }: TabsProps) => {
13
+ const Tabs = ({ selectedTab, onChangeTab, features, className }: TabsProps) => {
13
14
  const titles = useFeatureTitles(features);
14
15
 
16
+ let classes = styles.tabs;
17
+ if (className) {
18
+ classes += ` ${className}`;
19
+ }
20
+
15
21
  return (
16
- <ul className={styles.tabs}>
22
+ <ul className={classes}>
17
23
  {features.recentObservations && (
18
24
  <li onClick={() => onChangeTab(Tab.recent)}
19
25
  className={selectedTab === Tab.recent ? styles.selected : ""}>
@@ -1,5 +1,5 @@
1
1
  import React, {useState} from "react";
2
- import {getSourceFile, Tab, DataSource, Feature} from "../../__shared";
2
+ import {getSourceFile, Tab, DataSource, Feature, ConfigFile, BaseClasses, TabDescs} from "../../__shared";
3
3
  import {RecentObservations, RecentObservationsProps} from '../recent-observations/recent-observations';
4
4
  import {CommonTaxa} from '../common-taxa/common-taxa';
5
5
  import {Favourites} from "../favourites/favourites"
@@ -9,21 +9,36 @@ import Tabs from "../tabs/tabs";
9
9
  import {useFeatureTitles} from "../hooks/hooks";
10
10
  import styles from '../shared/css/general.module.scss';
11
11
 
12
- const TaxonPanel = ({ taxonId, placeId, dataSource, config, baseURL }: any) => {
12
+ export type TaxonPanelProps = {
13
+ taxonId: number;
14
+ placeId: number;
15
+ dataSource: DataSource;
16
+ config: ConfigFile;
17
+ baseUrl: string;
18
+ itemWidth?: number;
19
+ classes?: BaseClasses;
20
+ tabDescs?: TabDescs;
21
+ }
22
+
23
+ const TaxonPanel = ({ taxonId, placeId, dataSource, config, baseUrl, itemWidth, classes, tabDescs }: TaxonPanelProps) => {
13
24
  const [tab, setTab] = useState(Tab.recent);
14
25
  const [year, setYear] = useState<string>("all");
15
26
  const titles = useFeatureTitles(config.features);
16
- const [taxonInfo, setTaxonInfo] = useState(() => config.taxa.find((i) => i.taxonId === taxonId));
17
- const [placeInfo, setPlaceInfo] = useState(() => config.places.find((i) => i.placeId === placeId));
27
+ const [taxonInfo] = useState(() => config.taxa.find((i) => i.taxonId === taxonId));
28
+ const [placeInfo] = useState(() => config.places.find((i) => i.placeId === placeId));
18
29
 
30
+ console.log("..." , classes);
19
31
  const getCurrentTab = () => {
20
32
  switch (tab) {
21
33
  case Tab.recent: {
22
34
  const props: Partial<RecentObservationsProps> = {
23
- source: dataSource
35
+ source: dataSource,
36
+ itemWidth,
37
+ classes,
38
+ tabDesc: tabDescs?.recentDesc
24
39
  };
25
40
  if (dataSource === DataSource.url) {
26
- props.dataUrl = baseURL + '/' + getSourceFile(Feature.recentObservations, taxonInfo, placeInfo);
41
+ props.dataUrl = baseUrl + '/' + getSourceFile(Feature.recentObservations, taxonInfo, placeInfo);
27
42
  } else {
28
43
  props.placeId = placeId;
29
44
  props.taxonId = taxonId;
@@ -35,11 +50,14 @@ const TaxonPanel = ({ taxonId, placeId, dataSource, config, baseURL }: any) => {
35
50
 
36
51
  case Tab.commonTaxa: {
37
52
  const props: any = {
53
+ classes,
38
54
  source: dataSource,
39
- year
55
+ itemWidth,
56
+ year,
57
+ tabDesc: tabDescs?.mostCommonDesc
40
58
  };
41
59
  if (dataSource === DataSource.url) {
42
- props.dataUrl = baseURL + '/' + getSourceFile(Feature.commonTaxa, taxonInfo, placeInfo, year);
60
+ props.dataUrl = baseUrl + '/' + getSourceFile(Feature.commonTaxa, taxonInfo, placeInfo, year);
43
61
  } else {
44
62
  props.placeId = placeId;
45
63
  props.taxonId = taxonId;
@@ -51,11 +69,14 @@ const TaxonPanel = ({ taxonId, placeId, dataSource, config, baseURL }: any) => {
51
69
 
52
70
  case Tab.favourites: {
53
71
  const props: any = {
72
+ classes,
73
+ itemWidth,
54
74
  source: dataSource,
55
- year
75
+ year,
76
+ tabDesc: tabDescs?.mostFavouritedDesc
56
77
  };
57
78
  if (dataSource === DataSource.url) {
58
- props.dataUrl = baseURL + '/' + getSourceFile(Feature.favourites, taxonInfo, placeInfo, year);
79
+ props.dataUrl = baseUrl + '/' + getSourceFile(Feature.favourites, taxonInfo, placeInfo, year);
59
80
  } else {
60
81
  props.placeId = placeId;
61
82
  props.taxonId = taxonId;
@@ -67,11 +88,13 @@ const TaxonPanel = ({ taxonId, placeId, dataSource, config, baseURL }: any) => {
67
88
 
68
89
  case Tab.stats: {
69
90
  const props: any = {
91
+ classes,
70
92
  source: dataSource,
71
- year
93
+ year,
94
+ tabDesc: tabDescs?.generalStatsDesc
72
95
  };
73
96
  if (dataSource === DataSource.url) {
74
- props.dataUrl = baseURL + '/' + getSourceFile(Feature.stats, taxonInfo, placeInfo, year);
97
+ props.dataUrl = baseUrl + '/' + getSourceFile(Feature.stats, taxonInfo, placeInfo, year);
75
98
  } else {
76
99
  props.placeId = placeId;
77
100
  props.taxonId = taxonId;
@@ -81,20 +104,24 @@ const TaxonPanel = ({ taxonId, placeId, dataSource, config, baseURL }: any) => {
81
104
  }
82
105
  };
83
106
 
107
+ const tabsClass = classes?.tabs || '';
108
+ const yearDropdownClass = classes?.yearsDropdown || '';
109
+
84
110
  return (
85
111
  <div className={styles.page}>
86
112
  <Tabs
87
113
  selectedTab={tab}
88
114
  onChangeTab={setTab}
89
115
  features={config.features}
116
+ className={tabsClass}
90
117
  />
91
118
  <div>
92
119
  {tab !== Tab.recent && (
93
- <div style={{ float: "right" }}>
120
+ <div style={{ float: "right" }} className={yearDropdownClass}>
94
121
  <Years value={year} onChange={setYear} />
95
122
  </div>
96
123
  )}
97
- <h1>{titles[tab]}</h1>
124
+ <h1 className={classes?.pageHeadings}>{titles[tab]}</h1>
98
125
  </div>
99
126
  {getCurrentTab()}
100
127
  </div>