Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
47 changes: 36 additions & 11 deletions docs/next-js/components/loading/LocalNetCDFMeta.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,40 +3,56 @@
import React, { ChangeEvent, useState } from 'react';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
// import { MetaNetCDFAccordion } from './MetaNetCDFAccordion';
import { MetaNetCDFButtons } from './MetaNetCDFButtons';
import {
Alert,
AlertDescription,
AlertTitle,
} from '@/components/ui/alert';
import { Terminal } from 'lucide-react';

import { MetaNetCDFButtons } from './MetaNetCDFButtons';
import { NetCDF4 } from '@earthyscience/netcdf4-wasm';
import BrowzarrCTA from './BrowzarrCTA';

const NETCDF_EXT_REGEX = /\.(nc|netcdf|nc3|nc4)$/i;

const LocalNetCDFMeta = () => {
const [variables, setVariables] = useState<Record<string, unknown> | null>(null);
const [attributes, setAttributes] = useState<Record<string, unknown> | null>(null);
const [metadata, setMetadata] = useState<Record<string, unknown>[] | null>(null);
const [error, setError] = useState<string | null>(null);

const handleFileSelect = async (
event: ChangeEvent<HTMLInputElement>
) => {
setError(null);

const files = event.target.files;
if (!files || files.length === 0) return;

const file = files[0];

// Manual validation (iOS-safe)
if (!NETCDF_EXT_REGEX.test(file.name)) {
setError('Please select a valid NetCDF (.nc, .netcdf, .nc3, .nc4) file.');
return;
}

try {
const data = await NetCDF4.fromBlobLazy(file);

const [variables, attrs, metadata] = await Promise.all([
const [vars, attrs, meta] = await Promise.all([
data.getVariables(),
data.getGlobalAttributes(),
data.getFullMetadata(),
]);

setVariables(variables);
setVariables(vars);
setAttributes(attrs);
setMetadata(metadata);

} catch (error) {
console.error('Error loading NetCDF file:', error);
alert('Failed to load NetCDF file. Check console for details.');
setMetadata(meta);
} catch (err) {
console.error('Error loading NetCDF file:', err);
setError('Failed to load NetCDF file. Check console for details.');
}
};

Expand All @@ -52,11 +68,20 @@ const LocalNetCDFMeta = () => {
<Input
id="netcdf-file"
type="file"
accept=".nc,.netcdf,.nc3,.nc4"
onChange={handleFileSelect}
className="cursor-pointer"
/>


{error && (
<Alert variant="destructive">
<Terminal className="h-4 w-4" />
<AlertTitle>Hey!</AlertTitle>
<AlertDescription>
{error}
</AlertDescription>
</Alert>
)}

{variables && attributes && metadata && (
<div className="justify-self-center">
<MetaNetCDFButtons
Expand Down
66 changes: 66 additions & 0 deletions docs/next-js/components/ui/alert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"

import { cn } from "@/lib/utils"

const alertVariants = cva(
"relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current",
{
variants: {
variant: {
default: "bg-card text-card-foreground",
destructive:
"text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90",
},
},
defaultVariants: {
variant: "default",
},
}
)

function Alert({
className,
variant,
...props
}: React.ComponentProps<"div"> & VariantProps<typeof alertVariants>) {
return (
<div
data-slot="alert"
role="alert"
className={cn(alertVariants({ variant }), className)}
{...props}
/>
)
}

function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="alert-title"
className={cn(
"col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight",
className
)}
{...props}
/>
)
}

function AlertDescription({
className,
...props
}: React.ComponentProps<"div">) {
return (
<div
data-slot="alert-description"
className={cn(
"text-muted-foreground col-start-2 grid justify-items-start gap-1 text-sm [&_p]:leading-relaxed",
className
)}
{...props}
/>
)
}

export { Alert, AlertTitle, AlertDescription }