diff --git a/src/components/ui/astronomy.tsx b/src/components/ui/astronomy.tsx
new file mode 100644
index 0000000..bc1bc43
--- /dev/null
+++ b/src/components/ui/astronomy.tsx
@@ -0,0 +1,101 @@
+import React, { ReactElement, ReactNode } from "react";
+
+interface QuantityProps {
+ value: string | number;
+ unit?: string;
+ className?: string;
+ spaced?: boolean;
+}
+
+export function Quantity({
+ value,
+ unit,
+ className,
+ spaced = true,
+}: QuantityProps): React.ReactElement {
+ return (
+
+ {value}
+ {unit ? (
+
+ {spaced ? " " : ""}
+ {unit}
+
+ ) : (
+ ""
+ )}
+
+ );
+}
+
+interface QuantityWithErrorProps {
+ children: ReactNode;
+ error: number;
+ unit?: string;
+ decimalPlaces?: number;
+}
+
+export function QuantityWithError({
+ children,
+ error,
+ unit,
+ decimalPlaces = 2,
+}: QuantityWithErrorProps): ReactElement {
+ return (
+
+ {children} ±
+
+ );
+}
+
+interface AstronomicalCoordinateProps {
+ value: number;
+ className?: string;
+}
+
+export function RightAscension({
+ value,
+ className,
+}: AstronomicalCoordinateProps): React.ReactElement {
+ if (isNaN(value)) {
+ return N/A;
+ }
+
+ const totalSeconds = value * 240;
+ const hours = Math.floor(totalSeconds / 3600);
+ const minutes = Math.floor((totalSeconds % 3600) / 60);
+ const seconds = +(totalSeconds % 60).toFixed(2);
+
+ return (
+
+ {" "}
+ {" "}
+
+
+ );
+}
+
+export function Declination({
+ value,
+ className,
+}: AstronomicalCoordinateProps): React.ReactElement {
+ if (isNaN(value)) {
+ return N/A;
+ }
+
+ const sign = value < 0 ? "-" : "+";
+ const absDec = Math.abs(value);
+ const degrees = Math.floor(absDec);
+ const minutesFloat = (absDec - degrees) * 60;
+ const minutes = Math.floor(minutesFloat);
+ const seconds = +(minutesFloat - minutes) * 60;
+
+ return (
+
+ {sign}
+ {" "}
+ {" "}
+
+
+ );
+}
diff --git a/src/components/ui/catalog-data.tsx b/src/components/ui/catalog-data.tsx
index f66134e..ee2c0a4 100644
--- a/src/components/ui/catalog-data.tsx
+++ b/src/components/ui/catalog-data.tsx
@@ -1,37 +1,25 @@
import { ReactElement } from "react";
-import { CommonTable } from "./common-table";
+import { CommonTable, Column } from "./common-table";
import { Catalogs, Schema } from "../../clients/backend/types.gen";
+import {
+ Declination,
+ RightAscension,
+ Quantity,
+ QuantityWithError,
+} from "./astronomy";
interface CatalogDataProps {
catalogs: Catalogs;
schema: Schema;
}
-function formatValueWithError(
- value: number | undefined,
- error: number | undefined,
- unit?: string,
- decimalPlaces: number = 0,
-): string {
- if (value === undefined) return "NULL";
- const formattedValue = value.toFixed(decimalPlaces);
- const formattedError =
- error?.toFixed(decimalPlaces) || "0".padEnd(decimalPlaces + 1, "0");
-
- if (!unit) {
- return `${formattedValue} ± ${formattedError}`;
- }
-
- return `${formattedValue} ${unit} ± ${formattedError} ${unit}`;
-}
-
export function CatalogData({
catalogs,
schema,
}: CatalogDataProps): ReactElement {
if (!catalogs) return ;
- const columns = [{ name: "Parameter" }, { name: "Value" }];
+ const columns: Column[] = [{ name: "Parameter" }, { name: "Value" }];
const data = [];
@@ -43,41 +31,61 @@ export function CatalogData({
}
if (catalogs?.coordinates) {
- data.push(
- {
+ if (catalogs.coordinates.equatorial?.ra !== undefined) {
+ data.push({
Parameter: "Equatorial RA",
- Value: formatValueWithError(
- catalogs.coordinates.equatorial?.ra,
- catalogs.coordinates.equatorial?.e_ra,
- schema.units.coordinates?.equatorial?.ra,
- 2,
+ Value: (
+
+
+
),
- },
- {
+ });
+ }
+
+ if (catalogs.coordinates.equatorial?.dec !== undefined) {
+ data.push({
Parameter: "Equatorial Dec",
- Value: formatValueWithError(
- catalogs.coordinates.equatorial?.dec,
- catalogs.coordinates.equatorial?.e_dec,
- schema.units.coordinates?.equatorial?.dec,
- 2,
+ Value: (
+
+
+
),
- },
+ });
+ }
+
+ data.push(
{
Parameter: "Galactic l",
- Value: formatValueWithError(
- catalogs.coordinates.galactic?.lon,
- catalogs.coordinates.galactic?.e_lon,
- schema.units.coordinates?.galactic?.lon,
- 2,
+ Value: (
+
+
+
),
},
{
Parameter: "Galactic b",
- Value: formatValueWithError(
- catalogs.coordinates.galactic?.lat,
- catalogs.coordinates.galactic?.e_lat,
- schema.units.coordinates?.galactic?.lat,
- 2,
+ Value: (
+
+
+
),
},
);
@@ -86,11 +94,10 @@ export function CatalogData({
if (catalogs?.redshift) {
data.push({
Parameter: "Redshift z",
- Value: formatValueWithError(
- catalogs.redshift.z,
- catalogs.redshift.e_z,
- undefined,
- 5,
+ Value: (
+
+ {catalogs.redshift.z?.toFixed(5) || "N/A"}
+
),
});
}
@@ -99,34 +106,58 @@ export function CatalogData({
data.push(
{
Parameter: "Heliocentric Velocity",
- Value: formatValueWithError(
- catalogs.velocity.heliocentric?.v,
- catalogs.velocity.heliocentric?.e_v,
- schema.units.velocity?.heliocentric?.v,
+ Value: (
+
+
+
),
},
{
Parameter: "Local Group Velocity",
- Value: formatValueWithError(
- catalogs.velocity.local_group?.v,
- catalogs.velocity.local_group?.e_v,
- schema.units.velocity?.local_group?.v,
+ Value: (
+
+
+
),
},
{
Parameter: "CMB (old) Velocity",
- Value: formatValueWithError(
- catalogs.velocity.cmb_old?.v,
- catalogs.velocity.cmb_old?.e_v,
- schema.units.velocity?.cmb_old?.v,
+ Value: (
+
+
+
),
},
{
Parameter: "CMB Velocity",
- Value: formatValueWithError(
- catalogs.velocity.cmb?.v,
- catalogs.velocity.cmb?.e_v,
- schema.units.velocity?.cmb?.v,
+ Value: (
+
+
+
),
},
);
diff --git a/src/components/ui/common-table.tsx b/src/components/ui/common-table.tsx
index b9a942e..4ed1ba8 100644
--- a/src/components/ui/common-table.tsx
+++ b/src/components/ui/common-table.tsx
@@ -1,4 +1,4 @@
-import React, { ReactElement } from "react";
+import React, { ReactElement, ReactNode } from "react";
import classNames from "classnames";
import { Hint } from "./hint";
@@ -6,7 +6,7 @@ export type CellPrimitive = ReactElement | string | number;
export interface Column {
name: string;
- renderCell?: (value: CellPrimitive) => ReactElement;
+ renderCell?: (value: CellPrimitive) => ReactNode;
hint?: ReactElement;
}
@@ -33,7 +33,7 @@ export function CommonTable({
children,
onRowClick,
}: CommonTableProps): ReactElement {
- function renderCell(value: CellPrimitive, column: Column): ReactElement {
+ function renderCell(value: CellPrimitive, column: Column): ReactNode {
if (column.renderCell) {
return column.renderCell(value);
}
diff --git a/src/components/ui/pagination.tsx b/src/components/ui/pagination.tsx
new file mode 100644
index 0000000..029036f
--- /dev/null
+++ b/src/components/ui/pagination.tsx
@@ -0,0 +1,33 @@
+import { ReactElement } from "react";
+import { Button } from "./button";
+
+type PaginationProps = {
+ page: number;
+ pageSize: number;
+ records: unknown[];
+ handlePageChange: (newPage: number) => void;
+};
+
+export function Pagination({
+ page,
+ pageSize,
+ records,
+ handlePageChange,
+}: PaginationProps): ReactElement {
+ return (
+
+
+
+ Page {page + 1} (showing {records.length} records)
+
+
+
+ );
+}
diff --git a/src/pages/CrossmatchResults.tsx b/src/pages/CrossmatchResults.tsx
index 97e2c11..44f956e 100644
--- a/src/pages/CrossmatchResults.tsx
+++ b/src/pages/CrossmatchResults.tsx
@@ -21,6 +21,7 @@ import { Loading } from "../components/ui/loading";
import { ErrorPage } from "../components/ui/error-page";
import { Link } from "../components/ui/link";
import { useDataFetching } from "../hooks/useDataFetching";
+import { Pagination } from "../components/ui/pagination";
interface CrossmatchFiltersProps {
tableName: string | null;
@@ -244,27 +245,17 @@ export function CrossmatchResultsPage(): ReactElement {
function Content(): ReactElement {
if (loading) return ;
if (error) return ;
+ if (!data?.records) return ;
return (
<>
-
-
-
- Page {page + 1} (showing {data?.records.length} records)
-
-
-
+
>
);
}
diff --git a/src/pages/SearchResults.tsx b/src/pages/SearchResults.tsx
index f3a8b98..936bb8b 100644
--- a/src/pages/SearchResults.tsx
+++ b/src/pages/SearchResults.tsx
@@ -11,6 +11,9 @@ import { ErrorPage, ErrorPageHomeButton } from "../components/ui/error-page";
import { useDataFetching } from "../hooks/useDataFetching";
import { querySimpleApiV1QuerySimpleGet } from "../clients/backend/sdk.gen";
import { QuerySimpleResponse } from "../clients/backend/types.gen";
+import { Link } from "../components/ui/link";
+import { Declination, RightAscension } from "../components/ui/astronomy";
+import { Pagination } from "../components/ui/pagination";
function searchHandler(navigate: NavigateFunction) {
return function f(query: string) {
@@ -48,74 +51,56 @@ function SearchResults({
{
name: "PGC",
renderCell: (value: React.ReactElement | string | number) => (
-
- {value}
-
+ {value}
),
},
+ { name: "Name" },
{
- name: "Name",
+ name: "RA",
renderCell: (value: React.ReactElement | string | number) => (
- {value || "N/A"}
- ),
- },
- {
- name: "RA (deg)",
- renderCell: (value: React.ReactElement | string | number) => (
-
- {typeof value === "number" ? value.toFixed(6) : value}
-
+
),
},
{
- name: "Dec (deg)",
+ name: "Dec",
renderCell: (value: React.ReactElement | string | number) => (
-
- {typeof value === "number" ? value.toFixed(6) : value}
-
+
),
},
];
+ function handlePageChange(newPage: number): void {
+ pageChangeHandler(navigate, query, pageSize, newPage);
+ }
+
if (results.objects.length > 0) {
return (
-
+ <>
({
PGC: object.pgc,
Name: object.catalogs.designation?.name || "N/A",
- "RA (deg)": object.catalogs.coordinates?.equatorial.ra || 0,
- "Dec (deg)": object.catalogs.coordinates?.equatorial.dec || 0,
+ RA: object.catalogs.coordinates?.equatorial.ra || 0,
+ Dec: object.catalogs.coordinates?.equatorial.dec || 0,
}))}
className="w-full"
- onRowClick={(row) => {
- const pgc = row.PGC as number;
- navigate(`/object/${pgc}`);
- }}
/>
-
-
- Page {page}
-
-
-
+
+ >
);
}
@@ -163,7 +148,7 @@ export function SearchResultsPage(): ReactElement {
const navigate = useNavigate();
const query = searchParams.get("q") || "";
const page = parseInt(searchParams.get("page") || "1");
- const pageSize = parseInt(searchParams.get("pagesize") || "10");
+ const pageSize = parseInt(searchParams.get("pagesize") || "25");
useEffect(() => {
document.title = `${query} | HyperLEDA`;