diff --git a/app/scripts/components/common/card-sources.tsx b/app/scripts/components/common/card-sources.tsx index d592a1c68..72d47dbd4 100644 --- a/app/scripts/components/common/card-sources.tsx +++ b/app/scripts/components/common/card-sources.tsx @@ -1,8 +1,7 @@ import React from 'react'; import styled from 'styled-components'; -import { Link } from 'react-router-dom'; import { listReset } from '@devseed-ui/theme-provider'; -import { TaxonomyItem } from '$types/veda'; +import { LinkProperties, TaxonomyItem } from '$types/veda'; import { FilterActions } from '$components/common//catalog/utils'; const SourcesUl = styled.ul` @@ -23,11 +22,12 @@ interface SourcesListProps { sources?: TaxonomyItem[]; onSourceClick?: (v: string) => void; rootPath?: string; + linkProperties: LinkProperties; } export function CardSourcesList(props: SourcesListProps) { - const { sources, onSourceClick, rootPath } = props; - + const { sources, onSourceClick, linkProperties, rootPath } = props; + const { LinkElement, pathAttributeKeyName } = linkProperties as { LinkElement: React.ElementType, pathAttributeKeyName: string }; if (!sources?.length) return null; // No link rendering @@ -50,19 +50,19 @@ export function CardSourcesList(props: SourcesListProps) { {sources.map((source) => (
  • - { e.preventDefault(); onSourceClick(source.id); }} > {source.name} - +
  • ))}
    diff --git a/app/scripts/components/common/catalog/catalog-card.tsx b/app/scripts/components/common/catalog/catalog-card.tsx index 439062461..65ef6b20e 100644 --- a/app/scripts/components/common/catalog/catalog-card.tsx +++ b/app/scripts/components/common/catalog/catalog-card.tsx @@ -121,7 +121,7 @@ export const CatalogCard = (props: CatalogCardProps) => { overline={ - + } linkLabel='View dataset' diff --git a/app/scripts/components/common/featured-slider-section.tsx b/app/scripts/components/common/featured-slider-section.tsx index e70a00b15..fb836d054 100644 --- a/app/scripts/components/common/featured-slider-section.tsx +++ b/app/scripts/components/common/featured-slider-section.tsx @@ -93,7 +93,10 @@ function FeaturedSliderSection(props: FeaturedSliderSectionProps) { return sortedFeaturedItems.map((d) => { const date = new Date(d[dateProperty ?? '']); const topics = getTaxonomy(d, TAXONOMY_TOPICS)?.values; - + const linkProperties = { + pathAttributeKeyName: 'to', + LinkElement: SmartLink + }; return ( )} diff --git a/app/scripts/components/common/map/controls/aoi/custom-aoi-control.tsx b/app/scripts/components/common/map/controls/aoi/custom-aoi-control.tsx index 34d8b90ec..98300ca8f 100644 --- a/app/scripts/components/common/map/controls/aoi/custom-aoi-control.tsx +++ b/app/scripts/components/common/map/controls/aoi/custom-aoi-control.tsx @@ -213,6 +213,7 @@ function CustomAoI({ // selected, the trash method doesn't do anything. So, in this case, we // trigger the delete for the whole feature. const selectedFeatures = mbDraw.getSelected()?.features; + if ( mbDraw.getMode() === DIRECT_SELECT && selectedFeatures.length && @@ -241,9 +242,19 @@ function CustomAoI({ mbDraw.trash(); }, [features, aoiDeleteAll, map]); - const isAreaSelected = !!map?._drawControl?.getSelected().features.length; - const isPointSelected = - !!map?._drawControl.getSelectedPoints().features.length; + let isAreaSelected = false; + let isPointSelected = false; + + // @NOTE: map?._drawControl?.getSelected() needs access to mapboxgl draw context store, + // but the function gets called before mapboxdraw store is initialized (before being added to map) resulting in an error + // Wrapping with try block so the values get subbed even when the store is not available + try { + isAreaSelected = !!(map?._drawControl?.getSelected().features.length); + isPointSelected = !!(map?._drawControl?.getSelectedPoints()?.features.length); + } catch(e) { + isAreaSelected = false; + isPointSelected = false; + } const hasFeatures = !!features.length; return ( @@ -330,7 +341,6 @@ export default function CustomAoIControl({ // as Mapbox Draw handles this internally when the drawing is completed useEffect(() => { if (!main) return; - const mbDraw = main._drawControl; if (!mbDraw) return; diff --git a/app/scripts/components/exploration/atoms/datasets.ts b/app/scripts/components/exploration/atoms/datasets.ts index ed9751f07..f78bbb18d 100644 --- a/app/scripts/components/exploration/atoms/datasets.ts +++ b/app/scripts/components/exploration/atoms/datasets.ts @@ -51,6 +51,7 @@ export const timelineDatasetsAtom = atomWithUrlValueStability< } // Reconcile the dataset with the internal data (from VEDA config files) // and then add the url stored settings. + // @TODO - replace datasetLayers const [reconciled] = reconcileDatasets([enc.id], datasetLayers, []); if (enc.settings) { reconciled.settings = enc.settings; diff --git a/app/scripts/components/exploration/atoms/dates.ts b/app/scripts/components/exploration/atoms/dates.ts index e79e979a1..022ebcaeb 100644 --- a/app/scripts/components/exploration/atoms/dates.ts +++ b/app/scripts/components/exploration/atoms/dates.ts @@ -28,8 +28,8 @@ export const selectedDateAtom = atomWithUrlValueStability({ if (!prev || !next) return prev === next; return prev.getTime() === next.getTime(); }, - dehydrate: (date) => { - return date?.toISOString() ?? ''; + dehydrate: (d) => { + return d?.toISOString() ?? ''; } }); diff --git a/app/scripts/components/exploration/components/dataset-selector-modal/index.tsx b/app/scripts/components/exploration/components/dataset-selector-modal/index.tsx index cc7bf02bd..6ba6cd34b 100644 --- a/app/scripts/components/exploration/components/dataset-selector-modal/index.tsx +++ b/app/scripts/components/exploration/components/dataset-selector-modal/index.tsx @@ -1,6 +1,5 @@ import React, { useCallback, useEffect, useState } from 'react'; import styled from 'styled-components'; -import { useAtom } from 'jotai'; import { Modal, ModalBody, @@ -8,16 +7,18 @@ import { ModalHeader } from '@devseed-ui/modal'; import { glsp, themeVal } from '@devseed-ui/theme-provider'; -import { timelineDatasetsAtom } from '../../atoms/datasets'; import { - reconcileDatasets + reconcileDatasets, + getLayersFromDataset } from '../../data-utils-no-faux-module'; -import { datasetLayers } from '../../data-utils'; +import { TimelineDataset } from '../../types.d.ts'; + import RenderModalHeader from './header'; import ModalFooterRender from './footer'; import CatalogContent from '$components/common/catalog/catalog-content'; import { useFiltersWithURLAtom } from '$components/common/catalog/controls/hooks/use-filters-with-query'; import { FilterActions } from '$components/common/catalog/utils'; + import { DatasetData, LinkProperties, DatasetLayer } from '$types/veda'; const DatasetModal = styled(Modal)` @@ -68,16 +69,20 @@ interface DatasetSelectorModalProps { linkProperties: LinkProperties; datasets: DatasetData[]; datasetPathName: string; + timelineDatasets: TimelineDataset[]; + setTimelineDatasets: (datasets: TimelineDataset[]) => void; } export function DatasetSelectorModal(props: DatasetSelectorModalProps) { - const { revealed, linkProperties, datasets, datasetPathName, close } = props; + const { revealed, linkProperties, datasets, datasetPathName, timelineDatasets, setTimelineDatasets, close } = props; const { LinkElement , pathAttributeKeyName } = linkProperties as { LinkElement: React.ElementType, pathAttributeKeyName: string }; - const [timelineDatasets, setTimelineDatasets] = useAtom(timelineDatasetsAtom); + const datasetLayers = getLayersFromDataset(datasets); + const [selectedIds, setSelectedIds] = useState( timelineDatasets.map((dataset) => dataset.data.id) ); + const enhancedDatasetLayers = datasetLayers.flatMap(e => e); // Use Jotai controlled atoms for query parameter manipulation on new E&A page const {search: searchTerm, taxonomies, onAction } = useFiltersWithURLAtom(); @@ -95,11 +100,12 @@ export function DatasetSelectorModal(props: DatasetSelectorModalProps) { const onConfirm = useCallback(() => { setTimelineDatasets( - reconcileDatasets(selectedIds, datasetLayers, timelineDatasets) + reconcileDatasets(selectedIds, enhancedDatasetLayers, timelineDatasets) ); onAction(FilterActions.CLEAR); close(); - }, [close, selectedIds, timelineDatasets, setTimelineDatasets, onAction]); + }, [close, selectedIds, timelineDatasets, enhancedDatasetLayers, setTimelineDatasets, onAction]); + const linkElementProps = {[pathAttributeKeyName]: datasetPathName}; return ( ` +// override with 'as' as LinkComponent +const ButtonStyleLink = styled.a` &&& { ${({ variation, size }) => createButtonStyles({ variation, size })} } @@ -101,6 +101,7 @@ const LayerInfoLinerModal = styled.div` export default function LayerInfoModal(props: LayerInfoModalProps) { const { revealed, close, layerData } = props; + const { parentData } = layerData; const dataCatalogPage = getDatasetPath(parentData.id); return ( @@ -132,7 +133,7 @@ export default function LayerInfoModal(props: LayerInfoModalProps) {
    } footerContent={ - + Open in Data Catalog } diff --git a/app/scripts/components/exploration/components/parent-dataset-link.tsx b/app/scripts/components/exploration/components/parent-dataset-link.tsx deleted file mode 100644 index 0764b7674..000000000 --- a/app/scripts/components/exploration/components/parent-dataset-link.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import React from 'react'; -import styled from 'styled-components'; -import { themeVal } from '@devseed-ui/theme-provider'; -import { Link } from 'react-router-dom'; -import { CollecticonDatasetLayers } from '$components/common/icons/dataset-layers'; -import { ParentDatset } from '$components/exploration/types.d.ts'; -import { getDatasetPath } from '$utils/routes'; - -const DatasetLink = styled(Link)<{size: string}>` - color: ${themeVal('color.link')}; - text-align: left; - text-transform: none; - font-size: ${(props => props.size=='small'? '0.75rem': '1rem')}; - line-height: 0.75rem; - font-weight: normal; - display: flex; - align-items: center; - justify-content: center; - text-decoration: none; - gap: 0.1rem; - > svg { - fill: ${themeVal('color.link')}; - } -`; - -export default function ParentDatasetLink(props: { parentDataset: ParentDatset, size: string}) { - const { parentDataset, size } = props; - const linkTo = getDatasetPath(parentDataset.id); - return ( - - {parentDataset.name} - ); -} \ No newline at end of file diff --git a/app/scripts/components/exploration/container.tsx b/app/scripts/components/exploration/container.tsx index b04936029..434dcd457 100644 --- a/app/scripts/components/exploration/container.tsx +++ b/app/scripts/components/exploration/container.tsx @@ -3,9 +3,10 @@ import { TourProvider } from '@reactour/tour'; import { DevTools } from 'jotai-devtools'; import { useAtom } from 'jotai'; import { PopoverTourComponent, TourManager } from './tour-manager'; -import { timelineDatasetsAtom } from './atoms/datasets'; + import { DatasetSelectorModal } from './components/dataset-selector-modal'; import { allExploreDatasets } from './data-utils'; +import useTimelineDatasetAtom from './hooks/use-timeline-dataset-atom'; import ExplorationAndAnalysis from '.'; import { urlAtom } from '$utils/params-location-atom/url'; import { DATASETS_PATH, EXPLORATION_PATH } from '$utils/routes'; @@ -14,6 +15,7 @@ import { PageMainContent } from '$styles/page'; import { LayoutProps } from '$components/common/layout-root'; import PageHero from '$components/common/page-hero'; import SmartLink from '$components/common/smart-link'; + /** * @VEDA2-REFACTOR-WORK * @@ -30,15 +32,15 @@ const tourProviderStyles = { }; export default function ExplorationAndAnalysisContainer() { - const [datasets, setDatasets] = useAtom(timelineDatasetsAtom); + 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 [datasetModalRevealed, setDatasetModalRevealed] = useState( - !datasets.length - ); - if(!currentUrl.pathname?.includes(EXPLORATION_PATH)) return null; const openModal = () => setDatasetModalRevealed(true); @@ -59,11 +61,17 @@ export default function ExplorationAndAnalysisContainer() { - + ) => })); }); + export const getLayersFromDataset = (datasets: DatasetData[]) => + Object.values(datasets).map((dataset: DatasetData) => { + return dataset!.layers.map((l) => ({ + ...l, + parentDataset: { + id: dataset!.id, + name: dataset!.name + } + })); + }); /** * Returns an array of metrics based on the given Dataset Layer configuration. * If the layer has metrics defined, it returns only the metrics that match the diff --git a/app/scripts/components/exploration/hooks/use-timeline-dataset-atom.tsx b/app/scripts/components/exploration/hooks/use-timeline-dataset-atom.tsx new file mode 100644 index 000000000..46811c17d --- /dev/null +++ b/app/scripts/components/exploration/hooks/use-timeline-dataset-atom.tsx @@ -0,0 +1,11 @@ +import { useAtom } from 'jotai'; +import { timelineDatasetsAtom } from '../atoms/datasets'; +import { TimelineDataset } from '../types.d.ts'; + +export default function useTimelineDatasetAtom (): [ + TimelineDataset[], + (datasets: TimelineDataset[]) => void +] { + const [datasets, setDatasets] = useAtom(timelineDatasetsAtom); + return [datasets, setDatasets]; +} \ No newline at end of file diff --git a/app/scripts/components/stories/hub/hub-content.tsx b/app/scripts/components/stories/hub/hub-content.tsx index bcfa813a7..e78ea28f8 100644 --- a/app/scripts/components/stories/hub/hub-content.tsx +++ b/app/scripts/components/stories/hub/hub-content.tsx @@ -149,6 +149,7 @@ export default function HubContent(props:HubContentProps) { { onAction(FilterActions.TAXONOMY_MULTISELECT, { key: TAXONOMY_SOURCE, @@ -166,9 +167,8 @@ export default function HubContent(props:HubContentProps) { } linkLabel='View more' linkProperties={{ + ...linkProperties, linkTo: `${d.asLink?.url ?? d.path}`, - LinkElement, - pathAttributeKeyName, isLinkExternal: d.isLinkExternal }} title={ diff --git a/app/scripts/index.ts b/app/scripts/index.ts index 35fe6cbf5..59c0dde25 100644 --- a/app/scripts/index.ts +++ b/app/scripts/index.ts @@ -16,9 +16,21 @@ import { PageMainContent } from '$styles/page'; import PageHero from '$components/common/page-hero'; import { useFiltersWithQS } from '$components/common/catalog/controls/hooks/use-filters-with-query'; import ExplorationAndAnalysis from '$components/exploration'; +import useTimelineDatasetAtom from '$components/exploration/hooks/use-timeline-dataset-atom'; import { timelineDatasetsAtom } from '$components/exploration/atoms/datasets'; import { DatasetSelectorModal } from '$components/exploration/components/dataset-selector-modal'; +// Adding .last property to array +/* eslint-disable-next-line fp/no-mutating-methods */ +Object.defineProperty(Array.prototype, 'last', { + enumerable: false, + configurable: true, + get: function () { + return this[this.length - 1]; + }, + set: undefined +}); + export { // COMPONENTS Block, @@ -41,6 +53,7 @@ export { ExplorationAndAnalysis, DatasetSelectorModal, // HOOKS + useTimelineDatasetAtom, useFiltersWithQS, // STATE timelineDatasetsAtom