diff --git a/app/scripts/components/analysis/results/analysis-head-actions.tsx b/app/scripts/components/analysis/results/analysis-head-actions.tsx
deleted file mode 100644
index 0372793f1..000000000
--- a/app/scripts/components/analysis/results/analysis-head-actions.tsx
+++ /dev/null
@@ -1,199 +0,0 @@
-import React, { Fragment } from 'react';
-import styled, { useTheme } from 'styled-components';
-import { glsp, media, themeVal } from '@devseed-ui/theme-provider';
-import { Dropdown, DropTitle } from '@devseed-ui/dropdown';
-import { Button } from '@devseed-ui/button';
-import { CollecticonChevronDownSmall } from '@devseed-ui/collecticons';
-import { FormSwitch } from '@devseed-ui/form';
-
-import { FoldHeadActions } from '$components/common/fold';
-import {
- Legend,
- LegendTitle,
- LegendList,
- LegendSwatch,
- LegendLabel
-} from '$styles/infographics';
-
-export interface DataMetric {
- id: string;
- label: string;
- chartLabel: string;
- themeColor:
- | 'infographicA'
- | 'infographicB'
- | 'infographicC'
- | 'infographicD'
- | 'infographicE';
-}
-
-export const dataMetrics: DataMetric[] = [
- {
- id: 'min',
- label: 'Min',
- chartLabel: 'Min',
- themeColor: 'infographicA'
- },
- {
- id: 'mean',
- label: 'Average',
- chartLabel: 'Avg',
- themeColor: 'infographicB'
- },
- {
- id: 'max',
- label: 'Max',
- chartLabel: 'Max',
- themeColor: 'infographicC'
- },
- {
- id: 'std',
- label: 'St Deviation',
- chartLabel: 'STD',
- themeColor: 'infographicD'
- },
- {
- id: 'median',
- label: 'Median',
- chartLabel: 'Median',
- themeColor: 'infographicE'
- }
-];
-
-const MetricList = styled.ul`
- display: flex;
- flex-flow: column;
- list-style: none;
- margin: 0 -${glsp()};
- padding: 0;
- gap: ${glsp(0.5)};
-
- > li {
- padding: ${glsp(0, 1)};
- }
-`;
-
-const MetricSwitch = styled(FormSwitch)<{ metricThemeColor: string }>`
- display: grid;
- grid-template-columns: min-content 1fr auto;
-
- &::before {
- content: '';
- width: 0.5rem;
- height: 0.5rem;
- background: ${({ metricThemeColor }) =>
- themeVal(`color.${metricThemeColor}` as any)};
- border-radius: ${themeVal('shape.ellipsoid')};
- align-self: center;
- }
-`;
-
-const AnalysisFoldHeadActions = styled(FoldHeadActions)`
- width: 100%;
-
- ${media.mediumUp`
- width: auto;
- `}
-
- ${Button} {
- margin-left: auto;
- }
-`;
-
-const AnalysisLegend = styled(Legend)`
- flex-flow: column nowrap;
- align-items: flex-start;
-
- ${media.smallUp`
- flex-flow: row nowrap;
- align-items: center;
- `};
-`;
-
-const AnalysisLegendList = styled(LegendList)`
- display: grid;
- grid-template-columns: repeat(6, auto);
-
- ${media.smallUp`
- display: flex;
- flex-flow: row nowrap;
- `};
-`;
-
-interface AnalysisHeadActionsProps {
- activeMetrics: DataMetric[];
- onMetricsChange: (metrics: DataMetric[]) => void;
-}
-
-export default function AnalysisHeadActions(props: AnalysisHeadActionsProps) {
- const { activeMetrics, onMetricsChange } = props;
- const theme = useTheme();
-
- const handleMetricChange = (metric: DataMetric, shouldAdd: boolean) => {
- onMetricsChange(
- shouldAdd
- ? activeMetrics.concat(metric)
- : activeMetrics.filter((m) => m.id !== metric.id)
- );
- };
-
- return (
-
-
- Legend
-
- {dataMetrics.map((metric) => {
- const active = !!activeMetrics.find((m) => m.id === metric.id);
- return (
-
-
-
-
- {metric.label}
-
- );
- })}
-
-
-
- (
-
- )}
- >
- View options
-
- {dataMetrics.map((metric) => {
- const checked = !!activeMetrics.find((m) => m.id === metric.id);
- return (
-
- handleMetricChange(metric, !checked)}
- >
- {metric.label}
-
-
- );
- })}
-
-
-
- );
-}
diff --git a/app/scripts/components/analysis/results/analysis-head.tsx b/app/scripts/components/analysis/results/analysis-head.tsx
new file mode 100644
index 000000000..19f803269
--- /dev/null
+++ b/app/scripts/components/analysis/results/analysis-head.tsx
@@ -0,0 +1,79 @@
+import React, { Fragment } from 'react';
+import styled, { useTheme } from 'styled-components';
+import { media } from '@devseed-ui/theme-provider';
+import { Button } from '@devseed-ui/button';
+
+import { DATA_METRICS } from './analysis-metrics-dropdown';
+
+import { FoldHeadActions } from '$components/common/fold';
+import {
+ Legend,
+ LegendTitle,
+ LegendList,
+ LegendSwatch,
+ LegendLabel
+} from '$styles/infographics';
+
+const AnalysisFoldHeadActions = styled(FoldHeadActions)`
+ width: 100%;
+
+ ${media.mediumUp`
+ width: auto;
+ `}
+
+ ${Button} {
+ margin-left: auto;
+ }
+`;
+
+const AnalysisLegend = styled(Legend)`
+ flex-flow: column nowrap;
+ align-items: flex-start;
+
+ ${media.smallUp`
+ flex-flow: row nowrap;
+ align-items: center;
+ `};
+`;
+
+const AnalysisLegendList = styled(LegendList)`
+ display: grid;
+ grid-template-columns: repeat(6, auto);
+
+ ${media.smallUp`
+ display: flex;
+ flex-flow: row nowrap;
+ `};
+`;
+
+export default function AnalysisHead() {
+ const theme = useTheme();
+
+ return (
+
+
+ Legend
+
+ {DATA_METRICS.map((metric) => {
+ return (
+
+
+
+
+ {metric.label}
+
+ );
+ })}
+
+
+
+ );
+}
diff --git a/app/scripts/components/analysis/results/analysis-metrics-dropdown.tsx b/app/scripts/components/analysis/results/analysis-metrics-dropdown.tsx
new file mode 100644
index 000000000..bf2b268ea
--- /dev/null
+++ b/app/scripts/components/analysis/results/analysis-metrics-dropdown.tsx
@@ -0,0 +1,139 @@
+import React from 'react';
+import styled from 'styled-components';
+import { glsp, themeVal } from '@devseed-ui/theme-provider';
+import { Dropdown, DropTitle } from '@devseed-ui/dropdown';
+import { Button } from '@devseed-ui/button';
+import { CollecticonChartLine } from '@devseed-ui/collecticons';
+import { FormSwitch } from '@devseed-ui/form';
+
+export interface DataMetric {
+ id: string;
+ label: string;
+ chartLabel: string;
+ themeColor:
+ | 'infographicA'
+ | 'infographicB'
+ | 'infographicC'
+ | 'infographicD'
+ | 'infographicE';
+}
+
+export const DATA_METRICS: DataMetric[] = [
+ {
+ id: 'min',
+ label: 'Min',
+ chartLabel: 'Min',
+ themeColor: 'infographicA'
+ },
+ {
+ id: 'mean',
+ label: 'Average',
+ chartLabel: 'Avg',
+ themeColor: 'infographicB'
+ },
+ {
+ id: 'max',
+ label: 'Max',
+ chartLabel: 'Max',
+ themeColor: 'infographicC'
+ },
+ {
+ id: 'std',
+ label: 'St Deviation',
+ chartLabel: 'STD',
+ themeColor: 'infographicD'
+ },
+ {
+ id: 'median',
+ label: 'Median',
+ chartLabel: 'Median',
+ themeColor: 'infographicE'
+ }
+];
+
+const MetricList = styled.ul`
+ display: flex;
+ flex-flow: column;
+ list-style: none;
+ margin: 0 -${glsp()};
+ padding: 0;
+ gap: ${glsp(0.5)};
+
+ > li {
+ padding: ${glsp(0, 1)};
+ }
+`;
+
+const MetricSwitch = styled(FormSwitch)<{ metricThemeColor: string }>`
+ display: grid;
+ grid-template-columns: min-content 1fr auto;
+
+ &::before {
+ content: '';
+ width: 0.5rem;
+ height: 0.5rem;
+ background: ${({ metricThemeColor }) =>
+ themeVal(`color.${metricThemeColor}` as any)};
+ border-radius: ${themeVal('shape.ellipsoid')};
+ align-self: center;
+ }
+`;
+
+interface AnalysisMetricsDropdownProps {
+ activeMetrics: DataMetric[];
+ onMetricsChange: (metrics: DataMetric[]) => void;
+ isDisabled: boolean;
+}
+
+export default function AnalysisMetricsDropdown(
+ props: AnalysisMetricsDropdownProps
+) {
+ const { activeMetrics, onMetricsChange, isDisabled } = props;
+
+ const handleMetricChange = (metric: DataMetric, shouldAdd: boolean) => {
+ onMetricsChange(
+ shouldAdd
+ ? activeMetrics.concat(metric)
+ : activeMetrics.filter((m) => m.id !== metric.id)
+ );
+ };
+
+ return (
+ (
+
+ )}
+ >
+ View options
+
+ {DATA_METRICS.map((metric) => {
+ const checked = !!activeMetrics.find((m) => m.id === metric.id);
+ return (
+
+ handleMetricChange(metric, !checked)}
+ >
+ {metric.label}
+
+
+ );
+ })}
+
+
+ );
+}
diff --git a/app/scripts/components/analysis/results/chart-card.tsx b/app/scripts/components/analysis/results/chart-card.tsx
index 0cb54fb75..8eea39dbc 100644
--- a/app/scripts/components/analysis/results/chart-card.tsx
+++ b/app/scripts/components/analysis/results/chart-card.tsx
@@ -1,4 +1,13 @@
-import React, { useCallback, useRef, useMemo, MouseEvent, ReactNode } from 'react';
+import React, {
+ useCallback,
+ useRef,
+ useMemo,
+ MouseEvent,
+ ReactNode,
+ useState
+} from 'react';
+import { DatasetLayer } from 'veda';
+import { get } from 'lodash';
import { reverse } from 'd3';
import styled, { useTheme } from 'styled-components';
import { Link } from 'react-router-dom';
@@ -23,7 +32,11 @@ import {
ChartCardNoData,
ChartCardNoMetric
} from './chart-card-message';
-import { DataMetric } from './analysis-head-actions';
+import AnalysisMetricsDropdown, {
+ DataMetric,
+ DATA_METRICS
+} from './analysis-metrics-dropdown';
+
import {
CardSelf,
CardHeader,
@@ -56,10 +69,25 @@ const InfoTipContent = styled.div`
}
`;
+function getInitialMetrics(data: DatasetLayer): DataMetric[] {
+ const metricsIds = get(data, 'analysis.metrics', []);
+
+ const foundMetrics = metricsIds
+ .map((metric: string) => {
+ return DATA_METRICS.find((m) => m.id === metric);
+ })
+ .filter(Boolean);
+
+ if (!foundMetrics.length) {
+ return DATA_METRICS;
+ }
+
+ return foundMetrics;
+}
+
interface ChartCardProps {
title: ReactNode;
chartData: TimeseriesData;
- activeMetrics: DataMetric[];
availableDomain: [Date, Date];
brushRange: [Date, Date];
onBrushRangeChange: (range: [Date, Date]) => void;
@@ -93,29 +121,23 @@ const getNoDownloadReason = ({ status, data }: TimeseriesData) => {
*
* @returns Internal path for Link
*/
-const getDatasetOverviewPath = (
- layerId: string
-) => {
+const getDatasetOverviewPath = (layerId: string) => {
const dataset = allDatasetsProps.find((d) =>
d.layers.find((l) => l.id === layerId)
);
- return dataset
- ? getDatasetPath(dataset)
- : '/';
+ return dataset ? getDatasetPath(dataset) : '/';
};
export default function ChartCard(props: ChartCardProps) {
- const {
- title,
- chartData,
- activeMetrics,
- availableDomain,
- brushRange,
- onBrushRangeChange
- } = props;
+ const { title, chartData, availableDomain, brushRange, onBrushRangeChange } =
+ props;
const { status, meta, data, error, name, id, layer } = chartData;
+ const [activeMetrics, setActiveMetrics] = useState(
+ getInitialMetrics(layer)
+ );
+
const chartRef = useRef(null);
const noDownloadReason = getNoDownloadReason(chartData);
@@ -132,7 +154,10 @@ export default function ChartCard(props: ChartCardProps) {
// The indexes expect the data to be ascending, so we have to reverse the
// data.
const data = reverse(chartData.data.timeseries);
- const filename = `chart.${id}.${getDateRangeFormatted(startDate, endDate)}`;
+ const filename = `chart.${id}.${getDateRangeFormatted(
+ startDate,
+ endDate
+ )}`;
if (type === 'image') {
chartRef.current?.saveAsImage(filename);
@@ -206,7 +231,11 @@ export default function ChartCard(props: ChartCardProps) {
-
+
(dataMetrics);
-
useEffect(() => {
if (!start || !end || !datasetsLayers || !aoi) return;
@@ -186,10 +181,7 @@ export default function AnalysisResults() {
return (
-
+
Results
-
+
{!!requestStatus.length && availableDomain && brushRange && (
@@ -230,7 +219,6 @@ export default function AnalysisResults() {
setBrushRange(range)}
diff --git a/app/scripts/components/common/chart/analysis/utils.ts b/app/scripts/components/common/chart/analysis/utils.ts
index 05b9ca28f..357bc1c91 100644
--- a/app/scripts/components/common/chart/analysis/utils.ts
+++ b/app/scripts/components/common/chart/analysis/utils.ts
@@ -5,7 +5,7 @@ import {
chartAspectRatio
} from '$components/common/chart/constant';
import { TimeseriesDataUnit } from '$components/analysis/results/timeseries-data';
-import { DataMetric } from '$components/analysis/results/analysis-head-actions';
+import { DataMetric } from '$components/analysis/results/analysis-head';
import { TimeDensity } from '$context/layer-data';
const URL = window.URL;