Skip to content

Commit 08c7d12

Browse files
committed
Merge branch 'main' into columnnav-update
* main: Lithology tags can now be clickable or links Allow setting root component for tags; fix stories Handle entity tags in light mode Handle white background in light mode Styling Styling fix Selecting glitch fixed Edit entities Made functions for types Add overlay Clean up TypeList code Entity types deletable Select all entitties of certain type
2 parents d8b71f5 + 61aa3cd commit 08c7d12

File tree

8 files changed

+529
-95
lines changed

8 files changed

+529
-95
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
}

packages/feedback-components/src/extractions/index.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ export function getTagStyle(
5151
options: { highlighted?: boolean; inDarkMode?: boolean; active?: boolean }
5252
): CSSProperties {
5353
const _baseColor = asChromaColor(baseColor ?? "#fff");
54-
const { highlighted = true, inDarkMode = false, active = false } = options;
54+
const { highlighted = true, inDarkMode = useDarkMode().isEnabled, active = false } = options;
5555

5656
let mixAmount = highlighted ? 0.8 : 0.5;
5757
let backgroundAlpha = highlighted ? 0.8 : 0.2;
@@ -61,17 +61,23 @@ export function getTagStyle(
6161
backgroundAlpha = 1;
6262
}
6363

64-
const mixTarget = inDarkMode ? "white" : "black";
64+
const mixTarget = "black";
6565

6666
const color = active ? "#000" : _baseColor.mix(mixTarget, mixAmount).hex();
6767
const borderColor = highlighted
6868
? _baseColor.mix(mixTarget, mixAmount / 1.1).hex()
6969
: "transparent";
7070

71-
const backgroundColor = active ?
71+
let backgroundColor = active ?
7272
_baseColor.alpha(backgroundAlpha).hex() :
7373
normalizeColor(_baseColor.alpha(backgroundAlpha).hex());
7474

75+
// handle white backgrounds in light mode
76+
if (!inDarkMode && backgroundColor === "#ffffff") {
77+
console.log("Adjusting background color for light mode:", backgroundColor);
78+
backgroundColor = "#f0f0f0";
79+
}
80+
7581
return {
7682
color,
7783
backgroundColor,

packages/feedback-components/src/extractions/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
type EntityType = { name: string; color: string; id: number };
1+
type EntityType = { name: string; color: string; id: number, description?: string };
22
type Match = any;
33

44
export interface Entity {

0 commit comments

Comments
 (0)