diff --git a/src/components/ui/MainPanel/CuratedDatasets.tsx b/src/components/ui/MainPanel/CuratedDatasets.tsx deleted file mode 100644 index 689ced2d..00000000 --- a/src/components/ui/MainPanel/CuratedDatasets.tsx +++ /dev/null @@ -1,103 +0,0 @@ -"use client"; - -import React, { useState } from 'react'; -import { ZARR_CATALOG } from "@/assets/index"; - -import { - Command, - CommandEmpty, - CommandGroup, - CommandInput, - CommandItem, - CommandList, -} from "@/components/ui/command"; -import { ChevronDown } from 'lucide-react'; - -const PAGE_SIZE = 5; - -type Props = { - activeOption: string; - setActiveOption: (key: string) => void; - setInitStore: (v: string) => void; - onOpenDescription: () => void; -}; - -const CuratedDatasets = ({ - activeOption, - setActiveOption, - setInitStore, - onOpenDescription, -}: Props) => { - const [search, setSearch] = useState(''); - const [showAll, setShowAll] = useState(false); - - const filtered = ZARR_CATALOG.filter(ds => - ds.label.toLowerCase().includes(search.toLowerCase()) || - ds.subtitle.toLowerCase().includes(search.toLowerCase()) - ); - - const visible = showAll ? filtered : filtered.slice(0, PAGE_SIZE); - const hiddenCount = filtered.length - PAGE_SIZE; - const hasMore = hiddenCount > 0; - - return ( - - { - setSearch(v); - setShowAll(false); - }} - /> - - No datasets found. - - {visible.map(ds => ( - { - setActiveOption(ds.key); - setInitStore(ds.store); - onOpenDescription(); - }} - className={`flex flex-col items-start gap-0.5 mb-2 cursor-pointer ${ - activeOption === ds.key ? 'bg-accent' : '' - }`} - > - {ds.label} - - {ds.subtitle} - - - ))} - - {!showAll && hasMore && ( - setShowAll(true)} - className="flex items-center justify-center gap-1.5 text-xs text-muted-foreground cursor-pointer py-2 border-t border-dashed border-border mt-1 hover:text-foreground" - > - - {hiddenCount} more dataset{hiddenCount > 1 ? 's' : ''} available - - )} - - {showAll && hasMore && ( - setShowAll(false)} - className="flex items-center justify-center gap-1.5 text-xs text-muted-foreground cursor-pointer py-2 border-t border-dashed border-border mt-1 hover:text-foreground" - > - - Show less - - )} - - - - ); -}; - -export default CuratedDatasets; \ No newline at end of file diff --git a/src/components/ui/MainPanel/DataSetsModal.tsx b/src/components/ui/MainPanel/DataSetsModal.tsx index 4a70513b..4dab59bf 100644 --- a/src/components/ui/MainPanel/DataSetsModal.tsx +++ b/src/components/ui/MainPanel/DataSetsModal.tsx @@ -5,27 +5,33 @@ import { useShallow } from 'zustand/shallow'; import { Dialog, DialogContent, + DialogHeader, + DialogDescription, DialogTitle, } from "@/components/ui/dialog"; -import { - ButtonGroup, -} from "@/components/ui/button-group"; +import { ButtonGroup } from "@/components/ui/button-group"; import { Button } from "@/components/ui/button-enhanced"; import { DescriptionContent } from './DescriptionContent'; -import CuratedDatasets from './CuratedDatasets'; +import StoreCatalog from './StoreCatalog'; +import { ZARR_CATALOG, ICECHUNK_CATALOG } from "@/assets/index"; import RemoteZarr from './RemoteZarr'; import LocalContent from './LocalContent'; import RemoteIcechunk from './RemoteIcechunk'; -type Tab = 'curated' | 'remote' | 'local' | 'icechunk'; +type Tab = 'remote' | 'local' | 'icechunk'; const TABS: { value: Tab; label: string }[] = [ - { value: 'curated', label: 'Curated' }, - { value: 'remote', label: 'Remote Zarr' }, - { value: 'icechunk', label: 'Icechunk' }, - { value: 'local', label: 'Local' }, + { value: 'remote', label: 'Remote Zarr' }, + { value: 'icechunk', label: 'Icechunk' }, + { value: 'local', label: 'Local' }, ]; +const TAB_DESCRIPTIONS: Record = { + remote: 'Browse and open remote Zarr stores via URL or from the catalog.', + icechunk: 'Connect to versioned Icechunk stores for transactional data access.', + local: 'Open a Zarr dataset stored on your local filesystem.', +}; + type Props = { open: boolean; onOpenChange: (v: boolean) => void; @@ -34,8 +40,10 @@ type Props = { const DatasetsModal = ({ open, onOpenChange, isSafari }: Props) => { const [activeOption, setActiveOption] = useState(''); + const [selectedUrl, setSelectedUrl] = useState(''); + const [selectedIcechunkUrl, setSelectedIcechunkUrl] = useState(''); const [hasFetched, setHasFetched] = useState(false); - const [activeTab, setActiveTab] = useState('curated'); + const [activeTab, setActiveTab] = useState('remote'); const { initStore, setInitStore, setOpenVariables, status } = useGlobalStore( useShallow(state => ({ @@ -47,25 +55,46 @@ const DatasetsModal = ({ open, onOpenChange, isSafari }: Props) => { ); const showDescription = hasFetched && status === null; - const openDescription = () => setHasFetched(true); const handleTabChange = (tab: Tab) => { setActiveTab(tab); setHasFetched(false); + setSelectedUrl(''); + setSelectedIcechunkUrl(''); + setActiveOption(''); + }; + + const handleOpenChange = (v: boolean) => { + if (!v) { + setSelectedUrl(''); + setSelectedIcechunkUrl(''); + setActiveOption(''); + setHasFetched(false); + } + onOpenChange(v); }; return ( - + - Open Dataset + + Open dataset + {TAB_DESCRIPTIONS[activeTab]} + - {/* Header */}
-

- Open dataset -

- + {!showDescription && ( + <> +

+ Open dataset +

+

+ {TAB_DESCRIPTIONS[activeTab]} +

+ + )} + {TABS.map(({ value, label }) => (
-
- {activeTab === 'curated' && ( - - )} +
{activeTab === 'remote' && ( - + <> + + + )} {activeTab === 'local' && ( { /> )} {activeTab === 'icechunk' && ( - + <> + + + )} {showDescription && ( { type Props = { setInitStore: (v: string) => void; onOpenDescription: () => void; + selectedUrl?: string }; -const RemoteIcechunk = ({ setInitStore, onOpenDescription }: Props) => { - const [url, setUrl] = useState(''); +const RemoteIcechunk = ({ setInitStore, onOpenDescription, selectedUrl = '' }: Props) => { + const [url, setUrl] = useState(selectedUrl); const [showSettings, setShowSettings] = useState(false); @@ -155,7 +156,7 @@ const RemoteIcechunk = ({ setInitStore, onOpenDescription }: Props) => {
{/* URL + Fetch */} -
+
, string> = { @@ -33,14 +32,16 @@ type Props = { initStore: string; setInitStore: (v: string) => void; onOpenDescription: () => void; + selectedUrl?: string; }; -const RemoteZarr = ({ initStore, setInitStore, onOpenDescription }: Props) => { +const RemoteZarr = ({ initStore, setInitStore, onOpenDescription, selectedUrl = '' }: Props) => { const [showFetchOptions, setShowFetchOptions] = useState(false); const [preset, setPreset] = useState('none'); const [presetValue, setPresetValue] = useState(''); const [headers, setHeaders] = useState([{ key: '', value: '' }]); const [showCustom, setShowCustom] = useState(false); + const [urlValue, setUrlValue] = useState(selectedUrl); const addHeaderRow = () => setHeaders(h => [...h, { key: '', value: '' }]); const removeHeaderRow = (i: number) => setHeaders(h => h.filter((_, idx) => idx !== i)); @@ -73,17 +74,16 @@ const RemoteZarr = ({ initStore, setInitStore, onOpenDescription }: Props) => { className="flex flex-col gap-3" onSubmit={(e: React.FormEvent) => { e.preventDefault(); - const input = e.currentTarget.elements[0] as HTMLInputElement; - const url = input.value; - if (!url) return; + if (!urlValue) return; const fetchHandler = buildFetchHandler(); - if (fetchHandler && url.startsWith('http://')) { + if (fetchHandler && urlValue.startsWith('http://')) { useGlobalStore.getState().setStatus('Error: Cannot send auth headers over plain HTTP — use HTTPS.'); return; } + const fetchOptions = { - ...(fetchHandler && { fetch: fetchHandler}), + ...(fetchHandler && { fetch: fetchHandler }), }; useZarrStore.getState().setIcechunkOptions(null); @@ -93,19 +93,24 @@ const RemoteZarr = ({ initStore, setInitStore, onOpenDescription }: Props) => { useGlobalStore.getState().setStatus('Fetching...'); useZarrStore.getState().bumpFetchKey(); - setInitStore(url); + setInitStore(urlValue); onOpenDescription(); }} > {/* URL + Fetch */} -
- +
+ setUrlValue(e.target.value)} + />
- {/* FetchOptions */} + {/* fetchOptions toggle */}