Skip to content

Commit 61aa3cd

Browse files
authored
Merge pull request #99
Tag updates
2 parents 6755eb5 + 0a16364 commit 61aa3cd

File tree

3 files changed

+131
-40
lines changed

3 files changed

+131
-40
lines changed

packages/data-components/src/components/unit-details/lithology-tag.ts

Lines changed: 40 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
import h from "@macrostrat/hyper";
22
import { TagField } from "./base";
3-
import { Tag, TagSize } from "./tag";
3+
import { BaseTagProps, Tag, TagSize } from "./tag";
44
import { useMemo } from "react";
5+
import { Lithology } from "@macrostrat/api-types";
6+
import classNames from "classnames";
57

6-
interface LithologyTagProps {
7-
data: any;
8-
color?: string;
8+
interface LithologyTagProps extends Omit<BaseTagProps, "onClick" | "name"> {
9+
data: Lithology;
910
className?: string;
1011
expandOnHover?: boolean;
1112
size?: TagSize;
1213
features?: Set<LithologyTagFeature>;
13-
onClick?: (event: any) => void;
14+
onClick?: (event: any, data: Lithology) => void;
1415
}
1516

1617
export enum LithologyTagFeature {
@@ -22,8 +23,8 @@ export function LithologyTag({
2223
data,
2324
color,
2425
features,
25-
size,
26-
onClick,
26+
onClick: _onClick,
27+
...rest
2728
}: LithologyTagProps) {
2829
let proportion = null;
2930
const showProportion = features?.has(LithologyTagFeature.Proportion) ?? false;
@@ -44,22 +45,21 @@ export function LithologyTag({
4445
});
4546
}
4647

47-
const clickable = onClick != null;
48-
49-
const handleClick = (event: MouseEvent) => {
50-
if (onClick) {
51-
onClick({ event, data });
52-
}
53-
};
48+
const onClick = useMemo(() => {
49+
if (_onClick == null) return null;
50+
return (event: MouseEvent) => {
51+
_onClick(event, data);
52+
};
53+
}, [data, _onClick]);
5454

5555
return h(Tag, {
5656
prefix: atts,
5757
details: proportion,
5858
name: data.name,
59-
className: clickable ? " clickable" : "",
60-
size,
59+
className: classNames({ clickable: onClick != null }, "lithology-tag"),
6160
color: color ?? data.color,
62-
onClick: clickable ? handleClick : undefined,
61+
onClick,
62+
...rest,
6363
});
6464
}
6565

@@ -95,11 +95,17 @@ export function LithologyList({
9595
LithologyTagFeature.Attributes,
9696
]),
9797
onClickItem,
98+
getItemHref,
99+
className,
98100
}: {
99101
label?: string;
100102
lithologies: any[];
101103
features?: Set<LithologyTagFeature>;
102-
onClickItem?: (data: any) => void;
104+
// Optional function to handle click events on each item
105+
onClickItem?: (event: MouseEvent, data: Lithology) => void;
106+
// Optional function to get a link location for each item
107+
getItemHref?: (data: Lithology) => string | null | undefined;
108+
className?: string;
103109
}) {
104110
const sortedLiths = useMemo(() => {
105111
const l1 = [...lithologies];
@@ -109,7 +115,7 @@ export function LithologyList({
109115

110116
return h(
111117
TagField,
112-
{ label },
118+
{ label, className },
113119
sortedLiths.map((lith) => {
114120
let l1 = { ...lith };
115121
if (l1.prop == 0) {
@@ -119,9 +125,8 @@ export function LithologyList({
119125
return h(LithologyTag, {
120126
data: l1,
121127
features,
122-
onClick: onClickItem
123-
? (data) => onClickItem({ ...data, data: l1 })
124-
: undefined,
128+
onClick: onClickItem,
129+
href: getItemHref?.(lith),
125130
});
126131
})
127132
);
@@ -138,12 +143,21 @@ function lithologyComparison(a, b) {
138143
return dx;
139144
}
140145

141-
export function EnvironmentsList({ environments, onClickItem }) {
146+
export function EnvironmentsList({
147+
environments,
148+
onClickItem,
149+
getItemHref,
150+
label = "Environments",
151+
}) {
142152
return h(
143153
TagField,
144-
{ label: "Environments", className: "environments-list" },
145-
environments.map((lith: any) => {
146-
return h(LithologyTag, { data: lith, onClick: onClickItem });
154+
{ label, className: "environments-list" },
155+
environments.map((env: any) => {
156+
return h(LithologyTag, {
157+
data: env,
158+
onClick: onClickItem,
159+
href: getItemHref?.(env),
160+
});
147161
})
148162
);
149163
}

packages/data-components/src/components/unit-details/tag.ts

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ import { useInDarkMode } from "@macrostrat/ui-components";
22
import hyper from "@macrostrat/hyper";
33
import { getLuminanceAdjustedColorScheme } from "@macrostrat/color-utils";
44
import styles from "./tag.module.sass";
5-
import { ReactNode } from "react";
5+
import { ComponentType, ReactNode, JSX } from "react";
66
import chroma from "chroma-js";
7+
import classNames from "classnames";
78

89
const h = hyper.styled(styles);
910

@@ -26,45 +27,62 @@ export interface BaseTagProps {
2627
children?: ReactNode;
2728
size?: TagSize;
2829
color?: chroma.ChromaInput;
29-
onClick?: (data: any) => void;
30+
onClick?: (event: MouseEvent) => void;
31+
href?: string;
32+
component?: ComponentOrHTMLTagElement<any>;
3033
}
3134

35+
export type ComponentOrHTMLTagElement<T> =
36+
| ComponentType<T>
37+
| keyof JSX.IntrinsicElements;
38+
3239
export function Tag(props: BaseTagProps) {
3340
const inDarkMode = useInDarkMode();
3441
const {
3542
prefix,
3643
name,
3744
details,
38-
classNames = {},
3945
className,
4046
children,
4147
size,
4248
color,
4349
onClick,
50+
href,
51+
component,
4452
} = props;
4553

54+
let classes = props.classNames ?? {};
55+
56+
let _component: ComponentOrHTMLTagElement<any> = component ?? "span";
57+
if (href != null && component == null) {
58+
// If a href is provided, use an anchor tag by default
59+
_component = "a";
60+
}
61+
62+
// TODO: details and prefix might be better moved outside of the component...
4663
let _details = null;
4764
if (details != null) {
48-
_details = h("span.details", { className: classNames.details }, details);
65+
_details = h("span.details", { className: classes.details }, details);
4966
}
5067

51-
const mainTag = h("span.main", { className: classNames.main }, [
68+
const mainTag = h("span.main", { className: classes.main }, [
5269
h("span.name", name),
5370
children,
5471
_details,
5572
]);
5673

5774
let _prefix = null;
5875
if (prefix != null) {
59-
_prefix = h("span.prefix", { className: classNames.prefix }, prefix);
76+
_prefix = h("span.prefix", { className: classes.prefix }, prefix);
6077
}
6178

6279
return h(
63-
"span.tag",
80+
_component,
6481
{
65-
className,
82+
className: classNames(className, "tag"),
6683
style: buildTagStyle({ color, size, inDarkMode }),
6784
onClick,
85+
href,
6886
},
6987
[_prefix, mainTag]
7088
);

packages/data-components/src/components/unit-details/unit-details.stories.ts

Lines changed: 65 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,28 @@ import h from "@macrostrat/hyper";
33
import {
44
LithologyTag as _LithologyTag,
55
LithologyList as _LithologyList,
6+
LithologyTagFeature,
67
} from "./lithology-tag";
78
import { TagSize } from "./tag";
89
import {
910
DataField as _DataField,
1011
IntervalField as _IntervalField,
1112
} from "./base";
13+
import {
14+
useAPIResult,
15+
useToaster,
16+
ToasterContext,
17+
} from "@macrostrat/ui-components";
1218

1319
export default {
1420
title: "Data components/Unit details",
1521
component: _LithologyTag,
16-
// More on argTypes: https://storybook.js.org/docs/react/api/argtypes
22+
// More on argTypes: https://storybook.js.org/docs/react/api/argtypes,
23+
decorators: [
24+
(Story) => {
25+
return h(ToasterContext, {}, h(Story));
26+
},
27+
],
1728
} as Meta<any>;
1829

1930
export function DataField() {
@@ -59,7 +70,7 @@ LithologyTag.args = {
5970
size: "normal",
6071
onClick: (e) => {
6172
console.log("Clicked lith id:", e.lith_id);
62-
}
73+
},
6374
};
6475

6576
export { LithologyTag };
@@ -72,7 +83,7 @@ export const LithologyTagWithProportion = {
7283
lith_id: 2,
7384
prop: 0.5,
7485
},
75-
showProportion: true,
86+
features: new Set([LithologyTagFeature.Proportion]),
7687
size: "normal",
7788
},
7889
};
@@ -86,8 +97,10 @@ export const LithologyTagWithAtts = {
8697
prop: 0.125,
8798
atts: ["red", "purple"],
8899
},
89-
showProportion: true,
90-
showAttributes: true,
100+
features: new Set([
101+
LithologyTagFeature.Attributes,
102+
LithologyTagFeature.Proportion,
103+
]),
91104
size: TagSize.Normal,
92105
},
93106
};
@@ -108,6 +121,52 @@ export function LithologyList() {
108121
],
109122
onClickItem: (e) => {
110123
console.log("Clicked lith id:", e.lithId);
111-
}
124+
},
125+
});
126+
}
127+
128+
export function LithologyListClickable() {
129+
const toaster = useToaster();
130+
const liths = useAPIResult(
131+
"https://dev.macrostrat.org/api/v2/defs/lithologies",
132+
{
133+
lith_class: "sedimentary",
134+
},
135+
(res) => res.success.data
136+
);
137+
138+
if (liths == null) {
139+
return h("div", "Loading lithologies...");
140+
}
141+
142+
return h(_LithologyList, {
143+
lithologies: liths,
144+
onClickItem: (e, data) => {
145+
toaster.show({
146+
message: `Clicked lith ID: ${data.lith_id}`,
147+
intent: "success",
148+
});
149+
},
150+
});
151+
}
152+
153+
export function LithologyListWithLinks() {
154+
const liths = useAPIResult(
155+
"https://dev.macrostrat.org/api/v2/defs/lithologies",
156+
{
157+
lith_class: "sedimentary",
158+
},
159+
(res) => res.success.data
160+
);
161+
162+
if (liths == null) {
163+
return h("div", "Loading lithologies...");
164+
}
165+
166+
return h(_LithologyList, {
167+
lithologies: liths,
168+
getItemHref(data) {
169+
return `https://dev.macrostrat.org/lex/lithology/${data.lith_id}`;
170+
},
112171
});
113172
}

0 commit comments

Comments
 (0)