Skip to content

Commit 80872e5

Browse files
committed
Addded feedback files from Macrostrat web
1 parent 4d5460c commit 80872e5

File tree

12 files changed

+1283
-24
lines changed

12 files changed

+1283
-24
lines changed
Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
import styles from "./main.module.sass";
2+
import classNames from "classnames";
3+
import { Tag } from "@blueprintjs/core";
4+
import { Entity, EntityExt, Highlight, EntityType } from "./types";
5+
import { CSSProperties } from "react";
6+
import { asChromaColor } from "@macrostrat/color-utils";
7+
import hyper from "@macrostrat/hyper";
8+
9+
const h = hyper.styled(styles);
10+
11+
export function buildHighlights(
12+
entities: EntityExt[],
13+
parent: EntityExt | null
14+
): Highlight[] {
15+
let highlights = [];
16+
let parents = [];
17+
if (parent != null) {
18+
parents = [parent.id, ...(parent.parents ?? [])];
19+
}
20+
21+
for (const entity of entities) {
22+
highlights.push({
23+
start: entity.indices[0],
24+
end: entity.indices[1],
25+
text: entity.name,
26+
backgroundColor: entity.type.color ?? "#ddd",
27+
tag: entity.type.name,
28+
id: entity.id,
29+
parents,
30+
});
31+
highlights.push(...buildHighlights(entity.children ?? [], entity));
32+
}
33+
return highlights;
34+
}
35+
36+
export function enhanceData(extractionData, models, entityTypes) {
37+
return {
38+
...extractionData,
39+
model: models.get(extractionData.model_id),
40+
entities: extractionData.entities?.map((d) =>
41+
enhanceEntity(d, entityTypes)
42+
),
43+
};
44+
}
45+
46+
export function getTagStyle(
47+
baseColor: string,
48+
options: { highlighted?: boolean; inDarkMode?: boolean; active?: boolean }
49+
): CSSProperties {
50+
const _baseColor = asChromaColor(baseColor ?? "#ddd");
51+
const { highlighted = true, inDarkMode = false, active = false } = options;
52+
53+
let mixAmount = highlighted ? 0.8 : 0.5;
54+
let backgroundAlpha = highlighted ? 0.8 : 0.2;
55+
56+
if (active) {
57+
mixAmount = 1;
58+
backgroundAlpha = 1;
59+
}
60+
61+
const mixTarget = inDarkMode ? "white" : "black";
62+
63+
const color = _baseColor.mix(mixTarget, mixAmount).css();
64+
const borderColor = highlighted
65+
? _baseColor.mix(mixTarget, mixAmount / 2).css()
66+
: "transparent";
67+
68+
return {
69+
color,
70+
backgroundColor: _baseColor.alpha(backgroundAlpha).css(),
71+
boxSizing: "border-box",
72+
borderStyle: "solid",
73+
borderColor,
74+
borderWidth: "1px",
75+
fontWeight: active ? "bold" : "normal",
76+
};
77+
}
78+
79+
function enhanceEntity(
80+
entity: Entity,
81+
entityTypes: Map<number, EntityType>
82+
): EntityExt {
83+
return {
84+
...entity,
85+
type: addColor(entityTypes.get(entity.type), entity.match != null),
86+
children: entity.children?.map((d) => enhanceEntity(d, entityTypes)),
87+
};
88+
}
89+
90+
function addColor(entityType: EntityType, match = false) {
91+
let color = entityType.color ?? "#ddd";
92+
93+
color = asChromaColor(color).brighten(match ? 1 : 2);
94+
95+
return { ...entityType, color: color.css() };
96+
}
97+
98+
export function ExtractionContext({
99+
data,
100+
entityTypes,
101+
}: {
102+
data: any;
103+
entityTypes: Map<number, EntityType>;
104+
}) {
105+
const highlights = buildHighlights(data.entities);
106+
107+
return h("div", [
108+
h("p", h(HighlightedText, { text: data.paragraph_text, highlights })),
109+
h(ModelInfo, { data: data.model }),
110+
h(
111+
"ul.entities",
112+
data.entities.map((d) => h(ExtractionInfo, { data: d }))
113+
),
114+
]);
115+
}
116+
117+
export function ModelInfo({ data }) {
118+
return h("p.model-name", ["Model: ", h("code.bp5-code", data.name)]);
119+
}
120+
121+
export function EntityTag({
122+
data,
123+
highlighted = true,
124+
active = false,
125+
onClickType,
126+
}) {
127+
const { name, type, match } = data;
128+
const className = classNames(
129+
{
130+
matched: match != null,
131+
type: data.type.name,
132+
},
133+
"entity"
134+
);
135+
136+
const style = getTagStyle(type.color, { highlighted, active });
137+
138+
return h(Tag, { style, className }, [
139+
h("span.entity-name", name),
140+
" ",
141+
h(
142+
"code.entity-type.bp5-code",
143+
{
144+
onClick(evt) {
145+
if (active && onClickType != null) {
146+
onClickType(type);
147+
evt.stopPropagation();
148+
}
149+
},
150+
},
151+
[type.name, h(Match, { data: match })]
152+
),
153+
]);
154+
}
155+
156+
function ExtractionInfo({ data }: { data: EntityExt }) {
157+
const children = data.children ?? [];
158+
159+
return h("li.entity-row", [
160+
h(EntityTag, { data }),
161+
h.if(children.length > 0)([
162+
h(
163+
"ul.children",
164+
children.map((d) => h(ExtractionInfo, { data: d }))
165+
),
166+
]),
167+
]);
168+
}
169+
170+
function Match({ data }) {
171+
if (data == null) return null;
172+
const href = buildHref(data);
173+
return h([" ", h("a.match", { href }, `#${matchID(data)}`)]);
174+
}
175+
176+
function buildHref(match) {
177+
/** Build a URL for a matched term */
178+
if (match == null) return null;
179+
180+
if (match.strat_name_id != null) {
181+
return `/lex/strat-names/${match.strat_name_id}`;
182+
}
183+
184+
if (match.lith_id != null) {
185+
return `/lex/lithologies`;
186+
}
187+
188+
if (match.lith_att_id != null) {
189+
return `/lex/lithologies`;
190+
}
191+
192+
return null;
193+
}
194+
195+
function matchID(match) {
196+
if (match == null) return null;
197+
198+
for (const id of ["strat_name_id", "lith_id", "lith_att_id"]) {
199+
if (match[id]) {
200+
return match[id];
201+
}
202+
}
203+
return null;
204+
}
205+
206+
function HighlightedText(props: { text: string; highlights: Highlight[] }) {
207+
const { text, highlights = [] } = props;
208+
const parts = [];
209+
let start = 0;
210+
211+
const sortedHighlights = highlights.sort((a, b) => a.start - b.start);
212+
const deconflictedHighlights = sortedHighlights.map((highlight, i) => {
213+
if (i === 0) return highlight;
214+
const prev = sortedHighlights[i - 1];
215+
if (highlight.start < prev.end) {
216+
highlight.start = prev.end;
217+
}
218+
return highlight;
219+
});
220+
221+
for (const highlight of deconflictedHighlights) {
222+
const { start: s, end, ...rest } = highlight;
223+
parts.push(text.slice(start, s));
224+
parts.push(h("span.highlight", { style: rest }, text.slice(s, end)));
225+
start = end;
226+
}
227+
parts.push(text.slice(start));
228+
return h("span", parts);
229+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
.entities
2+
list-style: none
3+
padding-left: 0
4+
5+
.entities ul
6+
list-style: none
7+
8+
.entity
9+
margin: 0.2em 0 0.5em
10+
padding-right: 3px
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
type EntityType = string;
2+
type Match = any;
3+
4+
export interface Entity {
5+
id: number;
6+
name: string;
7+
type?: number;
8+
indices: [number, number];
9+
children: Entity[];
10+
match?: Match;
11+
}
12+
13+
export { EntityType };
14+
15+
export type Highlight = {
16+
start: number;
17+
end: number;
18+
tag?: string;
19+
text?: string;
20+
backgroundColor?: string;
21+
borderColor?: string;
22+
id: number;
23+
parents?: number[];
24+
};
25+
26+
export interface EntityExt extends Omit<Entity, "type" | "children"> {
27+
type: EntityType;
28+
children: EntityExt[];
29+
}

0 commit comments

Comments
 (0)