Skip to content

Commit 038a471

Browse files
committed
refactor: simplify value by extracting Section
1 parent e43e36a commit 038a471

File tree

14 files changed

+1947
-1981
lines changed

14 files changed

+1947
-1981
lines changed

src/components/cards/asset.tsx

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import { useEffect, useState } from "react";
2+
import { LuDownload } from "react-icons/lu";
3+
import {
4+
Button,
5+
ButtonGroup,
6+
Card,
7+
Checkbox,
8+
Collapsible,
9+
DataList,
10+
HStack,
11+
Image,
12+
Span,
13+
} from "@chakra-ui/react";
14+
import type { StacAsset } from "stac-ts";
15+
import { isCog, isVisual } from "../../utils/stac";
16+
import Properties from "../sections/properties";
17+
18+
export default function AssetCard({
19+
asset,
20+
cogTileHref,
21+
setCogTileHref,
22+
}: {
23+
asset: StacAsset;
24+
cogTileHref: string | undefined;
25+
setCogTileHref: (href: string | undefined) => void;
26+
}) {
27+
const [imageError, setImageError] = useState(false);
28+
const [checked, setChecked] = useState(false);
29+
// eslint-disable-next-line
30+
const { href, roles, type, title, ...properties } = asset;
31+
32+
useEffect(() => {
33+
setChecked(cogTileHref === asset.href);
34+
}, [cogTileHref, asset.href]);
35+
36+
return (
37+
<Card.Root size={"sm"} w="full">
38+
<Card.Header>
39+
{asset.title && <Card.Title>{asset.title}</Card.Title>}
40+
</Card.Header>
41+
<Card.Body gap={6}>
42+
{!imageError && (
43+
<Image src={asset.href} onError={() => setImageError(true)} />
44+
)}
45+
<DataList.Root orientation={"horizontal"}>
46+
{asset.roles && (
47+
<DataList.Item>
48+
<DataList.ItemLabel>Roles</DataList.ItemLabel>
49+
<DataList.ItemValue>{asset.roles?.join(", ")}</DataList.ItemValue>
50+
</DataList.Item>
51+
)}
52+
{asset.type && (
53+
<DataList.Item>
54+
<DataList.ItemLabel>Type</DataList.ItemLabel>
55+
<DataList.ItemValue>{asset.type}</DataList.ItemValue>
56+
</DataList.Item>
57+
)}
58+
</DataList.Root>
59+
{Object.keys(properties).length > 0 && (
60+
<Collapsible.Root>
61+
<Collapsible.Trigger>Properties</Collapsible.Trigger>
62+
<Collapsible.Content>
63+
<Properties properties={properties} />
64+
</Collapsible.Content>
65+
</Collapsible.Root>
66+
)}
67+
<HStack>
68+
{isCog(asset) && isVisual(asset) && (
69+
<Checkbox.Root
70+
checked={checked}
71+
onCheckedChange={(e) => {
72+
setChecked(!!e.checked);
73+
if (e.checked) setCogTileHref(asset.href);
74+
else setCogTileHref(undefined);
75+
}}
76+
>
77+
<Checkbox.HiddenInput />
78+
<Checkbox.Control />
79+
<Checkbox.Label>Visualize</Checkbox.Label>
80+
</Checkbox.Root>
81+
)}
82+
83+
<Span flex={"1"} />
84+
85+
<ButtonGroup size="sm" variant="outline">
86+
<Button asChild>
87+
<a href={asset.href} target="_blank">
88+
<LuDownload /> Download
89+
</a>
90+
</Button>
91+
</ButtonGroup>
92+
</HStack>
93+
</Card.Body>
94+
</Card.Root>
95+
);
96+
}

src/components/properties.tsx

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import type { ReactNode } from "react";
2+
import { LuFileJson } from "react-icons/lu";
3+
import { Code, DataList, Dialog, IconButton, Portal } from "@chakra-ui/react";
4+
5+
export interface PropertiesProps {
6+
properties: { [k: string]: unknown };
7+
}
8+
9+
export function Properties({ properties }: PropertiesProps) {
10+
return (
11+
<DataList.Root size={"sm"} orientation={"horizontal"}>
12+
{Object.keys(properties).map((key) => (
13+
<Property key={key} propertyKey={key} propertyValue={properties[key]} />
14+
))}
15+
</DataList.Root>
16+
);
17+
}
18+
19+
function Property({
20+
propertyKey,
21+
propertyValue,
22+
}: {
23+
propertyKey: string;
24+
propertyValue: unknown;
25+
}) {
26+
return (
27+
<DataList.Item>
28+
<DataList.ItemLabel>{propertyKey}</DataList.ItemLabel>
29+
<DataList.ItemValue>{getValue(propertyValue)}</DataList.ItemValue>
30+
</DataList.Item>
31+
);
32+
}
33+
34+
function getValue(value: unknown): ReactNode {
35+
switch (typeof value) {
36+
case "string":
37+
case "number":
38+
case "bigint":
39+
case "boolean":
40+
case "undefined":
41+
return value;
42+
case "object":
43+
return (
44+
<Dialog.Root size={"lg"}>
45+
<Dialog.Trigger asChild>
46+
<IconButton size={"2xs"} variant={"plain"} p={0}>
47+
<LuFileJson />
48+
</IconButton>
49+
</Dialog.Trigger>
50+
<Portal>
51+
<Dialog.Backdrop />
52+
<Dialog.Positioner>
53+
<Dialog.Content>
54+
<Dialog.Body>
55+
<pre>
56+
<Code>{JSON.stringify(value, null, 2)}</Code>
57+
</pre>
58+
</Dialog.Body>
59+
</Dialog.Content>
60+
</Dialog.Positioner>
61+
</Portal>
62+
</Dialog.Root>
63+
);
64+
case "symbol":
65+
case "function":
66+
return null;
67+
}
68+
}

src/components/section.tsx

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { type ReactNode } from "react";
2+
import { type IconType } from "react-icons/lib";
3+
import { Accordion, HStack, Icon } from "@chakra-ui/react";
4+
5+
export default function Section({
6+
title,
7+
TitleIcon,
8+
value,
9+
children,
10+
}: {
11+
title: ReactNode;
12+
TitleIcon: IconType;
13+
value: string;
14+
children: ReactNode;
15+
}) {
16+
return (
17+
<Accordion.Item value={value}>
18+
<Accordion.ItemTrigger>
19+
<HStack flex={"1"}>
20+
<Icon>
21+
<TitleIcon />
22+
</Icon>{" "}
23+
{title}
24+
</HStack>
25+
<Accordion.ItemIndicator />
26+
</Accordion.ItemTrigger>
27+
<Accordion.ItemContent>
28+
<Accordion.ItemBody>{children}</Accordion.ItemBody>
29+
</Accordion.ItemContent>
30+
</Accordion.Item>
31+
);
32+
}

src/components/sections/assets.tsx

Lines changed: 17 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,31 @@
1-
import { useEffect, useState } from "react";
2-
import { LuDownload } from "react-icons/lu";
3-
import {
4-
Button,
5-
ButtonGroup,
6-
Card,
7-
Checkbox,
8-
Collapsible,
9-
DataList,
10-
HStack,
11-
Image,
12-
Span,
13-
} from "@chakra-ui/react";
14-
import type { StacAsset } from "stac-ts";
15-
import Properties from "./properties";
1+
import { LuFiles } from "react-icons/lu";
2+
import { DataList } from "@chakra-ui/react";
163
import type { StacAssets } from "../../types/stac";
17-
import { isCog, isVisual } from "../../utils/stac";
4+
import AssetCard from "../cards/asset";
5+
import Section from "../section";
186

19-
export default function Assets({
20-
assets,
21-
cogTileHref,
22-
setCogTileHref,
23-
}: {
7+
interface AssetsProps {
248
assets: StacAssets;
259
cogTileHref: string | undefined;
2610
setCogTileHref: (href: string | undefined) => void;
27-
}) {
11+
}
12+
13+
export default function AssetsSection({ ...props }: AssetsProps) {
14+
return (
15+
<Section title="Assets" TitleIcon={LuFiles} value="assets">
16+
<Assets {...props} />
17+
</Section>
18+
);
19+
}
20+
21+
function Assets({ assets, cogTileHref, setCogTileHref }: AssetsProps) {
2822
return (
2923
<DataList.Root>
3024
{Object.keys(assets).map((key) => (
3125
<DataList.Item key={"asset-" + key}>
3226
<DataList.ItemLabel>{key}</DataList.ItemLabel>
3327
<DataList.ItemValue>
34-
<Asset
28+
<AssetCard
3529
asset={assets[key]}
3630
cogTileHref={cogTileHref}
3731
setCogTileHref={setCogTileHref}
@@ -42,83 +36,3 @@ export default function Assets({
4236
</DataList.Root>
4337
);
4438
}
45-
46-
function Asset({
47-
asset,
48-
cogTileHref,
49-
setCogTileHref,
50-
}: {
51-
asset: StacAsset;
52-
cogTileHref: string | undefined;
53-
setCogTileHref: (href: string | undefined) => void;
54-
}) {
55-
const [imageError, setImageError] = useState(false);
56-
const [checked, setChecked] = useState(false);
57-
// eslint-disable-next-line
58-
const { href, roles, type, title, ...properties } = asset;
59-
60-
useEffect(() => {
61-
setChecked(cogTileHref === asset.href);
62-
}, [cogTileHref, asset.href]);
63-
64-
return (
65-
<Card.Root size={"sm"} w="full">
66-
<Card.Header>
67-
{asset.title && <Card.Title>{asset.title}</Card.Title>}
68-
</Card.Header>
69-
<Card.Body gap={6}>
70-
{!imageError && (
71-
<Image src={asset.href} onError={() => setImageError(true)} />
72-
)}
73-
<DataList.Root orientation={"horizontal"}>
74-
{asset.roles && (
75-
<DataList.Item>
76-
<DataList.ItemLabel>Roles</DataList.ItemLabel>
77-
<DataList.ItemValue>{asset.roles?.join(", ")}</DataList.ItemValue>
78-
</DataList.Item>
79-
)}
80-
{asset.type && (
81-
<DataList.Item>
82-
<DataList.ItemLabel>Type</DataList.ItemLabel>
83-
<DataList.ItemValue>{asset.type}</DataList.ItemValue>
84-
</DataList.Item>
85-
)}
86-
</DataList.Root>
87-
{Object.keys(properties).length > 0 && (
88-
<Collapsible.Root>
89-
<Collapsible.Trigger>Properties</Collapsible.Trigger>
90-
<Collapsible.Content>
91-
<Properties properties={properties} />
92-
</Collapsible.Content>
93-
</Collapsible.Root>
94-
)}
95-
<HStack>
96-
{isCog(asset) && isVisual(asset) && (
97-
<Checkbox.Root
98-
checked={checked}
99-
onCheckedChange={(e) => {
100-
setChecked(!!e.checked);
101-
if (e.checked) setCogTileHref(asset.href);
102-
else setCogTileHref(undefined);
103-
}}
104-
>
105-
<Checkbox.HiddenInput />
106-
<Checkbox.Control />
107-
<Checkbox.Label>Visualize</Checkbox.Label>
108-
</Checkbox.Root>
109-
)}
110-
111-
<Span flex={"1"} />
112-
113-
<ButtonGroup size="sm" variant="outline">
114-
<Button asChild>
115-
<a href={asset.href} target="_blank">
116-
<LuDownload /> Download
117-
</a>
118-
</Button>
119-
</ButtonGroup>
120-
</HStack>
121-
</Card.Body>
122-
</Card.Root>
123-
);
124-
}

src/components/sections/catalogs.tsx

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,24 @@
1+
import { LuFolder } from "react-icons/lu";
12
import { Stack } from "@chakra-ui/react";
23
import type { StacCatalog } from "stac-ts";
34
import CatalogCard from "../cards/catalog";
5+
import Section from "../section";
46

5-
export default function Catalogs({
6-
catalogs,
7-
setHref,
8-
}: {
7+
interface CatalogsProps {
98
catalogs: StacCatalog[];
109
setHref: (href: string | undefined) => void;
11-
}) {
10+
}
11+
12+
export default function CatalogsSection({ catalogs, setHref }: CatalogsProps) {
13+
const title = `Catalogs (${catalogs.length})`;
14+
return (
15+
<Section title={title} TitleIcon={LuFolder} value={"catalogs"}>
16+
<Catalogs catalogs={catalogs} setHref={setHref} />
17+
</Section>
18+
);
19+
}
20+
21+
function Catalogs({ catalogs, setHref }: CatalogsProps) {
1222
return (
1323
<Stack>
1424
{catalogs.map((catalog) => (

0 commit comments

Comments
 (0)