Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 0 additions & 20 deletions app/scripts/components/exploration/atoms/embed.ts

This file was deleted.

32 changes: 32 additions & 0 deletions app/scripts/components/exploration/atoms/viewMode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { ViewMode } from '$components/exploration/types.d.ts';
import { atomWithUrlValueStability } from '$utils/params-location-atom/atom-with-url-value-stability';

const initialParams = new URLSearchParams(window.location.search);

const hydrateViewMode = (serialized: string | null) => {
if (serialized === 'simple') return serialized;
return 'default';
};

const dehydrateViewMode = (value: ViewMode) => {
return value ?? 'default';
};

/**
* Atom that manages the exploration view mode via URL parameter.
*
* - 'simple': Minimal view for embedding (no navigation/header)
* - 'default': Full exploration and analysis interface
*
* URL parameter: `?viewMode=simple` (defaults to 'default')
*
* @example
* const [viewMode] = useAtom(viewModeAtom);
*/
export const viewModeAtom = atomWithUrlValueStability<ViewMode>({
initialValue: hydrateViewMode(initialParams.get('viewMode')),
urlParam: 'viewMode',
hydrate: hydrateViewMode,
dehydrate: dehydrateViewMode,
areEqual: (prev, next) => prev === next
});
Comment on lines +6 to +32
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is largely the same as the embed atom, however, we're now managing a view mode (simple or default) vs a boolean. This allows for more view modes in the future

83 changes: 0 additions & 83 deletions app/scripts/components/exploration/container.tsx

This file was deleted.

215 changes: 85 additions & 130 deletions app/scripts/components/exploration/index.tsx
Original file line number Diff line number Diff line change
@@ -1,142 +1,97 @@
import React, { useEffect, useState } from 'react';
import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels';
import styled from 'styled-components';
import { themeVal } from '@devseed-ui/theme-provider';
import { TourProvider } from '@reactour/tour';

import React, { useState } from 'react';
import { Link } from 'react-router-dom';
import { DevTools } from 'jotai-devtools';
import { useAtom, useSetAtom } from 'jotai';
import Timeline from './components/timeline/timeline';
import { ExplorationMap } from './components/map';
import { useAnalysisController } from './hooks/use-analysis-data-request';
import { PopoverTourComponent, TourManager } from './tour-manager';
import { TimelineDataset } from './types.d.ts';
import { selectedCompareDateAtom, selectedDateAtom } from './atoms/dates';
import { CLEAR_LOCATION, urlAtom } from '$utils/params-location-atom/url';
import { legacyGlobalStyleCSSBlock } from '$styles/legacy-global-styles';

// @TODO: "height: 100%" Added for exploration container to show correctly in NextJs instance but investigate why this is needed and possibly work to remove
const Container = styled.div`
display: flex;
flex-flow: column;
flex-grow: 1;
height: 100%;

.panel-wrapper {
flex-grow: 1;
}

.panel {
display: flex;
flex-direction: column;
position: relative;
}
* {
${legacyGlobalStyleCSSBlock}
}
.panel-timeline {
box-shadow: 0 -1px 0 0 ${themeVal('color.base-100')};
}

.resize-handle {
flex: 0;
position: relative;
outline: none;
display: flex;
align-items: center;
justify-content: center;
width: 5rem;
margin: 0 auto -1.25rem auto;
padding: 0rem 0 0.25rem;
z-index: 1;

::before {
content: '';
display: block;
width: 2rem;
background: ${themeVal('color.base-200')};
height: 0.25rem;
border-radius: ${themeVal('shape.ellipsoid')};
}
}
`;

const tourProviderStyles = {
popover: (base) => ({
...base,
padding: '0',
background: 'none'
})
};
import { DatasetSelectorModal } from '$components/exploration/components/dataset-selector-modal';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is largely the same as the prior container but I updated all the relative path imports.

import useTimelineDatasetAtom from '$components/exploration/hooks/use-timeline-dataset-atom';
import { externalDatasetsAtom } from '$components/exploration/atoms/datasetLayers';
import { viewModeAtom } from '$components/exploration/atoms/viewMode';
import ExplorationAndAnalysisSimpleView from '$components/exploration/views/simple';
import ExplorationAndAnalysisDefaultView from '$components/exploration/views/default';
import { allExploreDatasets } from '$data-layer/datasets';
import { urlAtom } from '$utils/params-location-atom/url';
import { PageMainContent } from '$styles/page';
import { LayoutProps } from '$components/common/layout-root';
import PageHero from '$components/common/page-hero';
import { DATASETS_PATH, EXPLORATION_PATH } from '$utils/routes';

interface ExplorationAndAnalysisProps {
datasets: TimelineDataset[];
setDatasets: (datasets: TimelineDataset[]) => void;
openDatasetsSelectionModal?: () => void;
}

export default function ExplorationAndAnalysis(
props: ExplorationAndAnalysisProps
) {
const { datasets, setDatasets, openDatasetsSelectionModal } = props;

const [selectedDay, setSelectedDay] = useAtom(selectedDateAtom);

const [selectedCompareDay, setSelectedCompareDay] = useAtom(
selectedCompareDateAtom
/**
* Container component that manages exploration view routing and data state.
*
* Routes between two view modes based on URL parameter:
* - Simple view (`?viewMode=simple`): Minimal interface for embedding
* - Default view: Full exploration and analysis interface
*
* @LEGACY-SUPPORT
*
* @NOTE: This container component serves as a wrapper for the purpose of data management,
* this is ONLY to support current instances. veda2 instances can just use the direct
Copy link
Contributor Author

@ifsimicoded ifsimicoded Dec 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vgeorge what is current instances vs veda2? is current instances, veda as a submodule? and veda2 is the the next-veda-ui implementation as a library (instead of submodule)?

when you say current instances, if I read this in one year, will that still apply?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ifsimicoded my original intent with this change was just to rename the reference component, and I was also confused by the veda2 term. After digging through past tickets, I realized it refers to a refactor effort from last year to evolve the architecture. Since this wasn’t well documented, I created a new ADR to capture what was done and how it connects to the current architecture discussions in #1952.

* component, 'ExplorationAndAnalysisDefaultView', and manage data directly in their page views
*
* @returns {JSX.Element | null} Renders the appropriate view or null if not on exploration path
*/
export default function ExplorationAndAnalysisContainer() {
const setExternalDatasets = useSetAtom(externalDatasetsAtom);
setExternalDatasets(allExploreDatasets);
const [timelineDatasets, setTimelineDatasets] = useTimelineDatasetAtom();
const [datasetModalRevealed, setDatasetModalRevealed] = useState(
!timelineDatasets.length
);
// @NOTE: When Exploration page is preloaded (ex. Linked with react-router)
// atomWithLocation gets initialized outside of Exploration page and returns the previous page's value
// We check if url Atom actually returns the values for exploration page here.
const [currentUrl] = useAtom(urlAtom);
const [viewMode] = useAtom(viewModeAtom);

// @TECH-DEBT: panelHeight needs to be passed to work around Safari CSS
const [panelHeight, setPanelHeight] = useState(0);

const setUrl = useSetAtom(urlAtom);
const { reset: resetAnalysisController } = useAnalysisController();
if (!currentUrl.pathname?.includes(EXPLORATION_PATH)) return null;

// Reset atoms when leaving the page.
useEffect(() => {
return () => {
resetAnalysisController();
setUrl(CLEAR_LOCATION);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const openModal = () => setDatasetModalRevealed(true);
const closeModal = () => setDatasetModalRevealed(false);

return (
<TourProvider
steps={[]}
styles={tourProviderStyles}
ContentComponent={PopoverTourComponent}
>
<TourManager />
<Container>
<PanelGroup direction='vertical' className='panel-wrapper'>
<Panel
maxSize={75}
className='panel'
onResize={(size: number) => {
setPanelHeight(size);
}}
>
<ExplorationMap
datasets={datasets}
setDatasets={setDatasets}
selectedDay={selectedDay}
selectedCompareDay={selectedCompareDay}
<>
<DevTools />
<LayoutProps
title='Exploration'
description='Explore and analyze datasets'
hideFooter
{...(viewMode === 'simple' && { hideNav: true, hideHeader: true })}
/>
<PageMainContent>
<PageHero title='Exploration' isHidden />
{viewMode === 'simple' ? (
<ExplorationAndAnalysisSimpleView datasets={timelineDatasets} />
) : (
<>
<ExplorationAndAnalysisDefaultView
datasets={timelineDatasets}
setDatasets={setTimelineDatasets}
openDatasetsSelectionModal={openModal}
/>
</Panel>
<PanelResizeHandle className='resize-handle' />
<Panel maxSize={75} className='panel panel-timeline'>
<Timeline
datasets={datasets}
selectedDay={selectedDay}
setSelectedDay={setSelectedDay}
selectedCompareDay={selectedCompareDay}
setSelectedCompareDay={setSelectedCompareDay}
onDatasetAddClick={openDatasetsSelectionModal}
panelHeight={panelHeight}
<DatasetSelectorModal
revealed={datasetModalRevealed}
close={closeModal}
datasets={allExploreDatasets}
timelineDatasets={timelineDatasets}
setTimelineDatasets={setTimelineDatasets}
emptyStateContent={
<>
<p>
There are no datasets to show with the selected filters.
</p>
<p>
This tool allows the exploration and analysis of time-series
datasets in raster format. For a comprehensive list of
available datasets, please visit the{' '}
<Link to={DATASETS_PATH}>Data Catalog</Link>.
</p>
</>
}
/>
</Panel>
</PanelGroup>
</Container>
</TourProvider>
</>
)}
</PageMainContent>
</>
);
}
8 changes: 8 additions & 0 deletions app/scripts/components/exploration/types.d.ts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,3 +194,11 @@ export interface ZoomTransformPlain {
y: number;
k: number;
}

/**
* Exploration view mode.
*
* - 'simple': Minimal view for embedding
* - 'default': Full exploration interface
*/
export type ViewMode = 'simple' | 'default';
Loading
Loading