Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion packages/lithology-hierarchy/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import { apiV2Prefix } from "@macrostrat-web/settings";
import { Spinner } from "@blueprintjs/core";
import { useAPIResult, ErrorCallout } from "@macrostrat/ui-components";
import { useState } from "react";
import { nestLiths, Lith } from "./nest-data";
import { nestLiths, nestItems, Lith } from "./nest-data";
import Hierarchy from "./simple-hierarchy";
import LexHierarchyInner from "./lex-hierarchy";

const h = hyper.styled(styles);

Expand Down Expand Up @@ -35,3 +36,13 @@ export default function MacrostratLithologyHierarchy({ width, height }) {
]),
]);
}

export function LexHierarchy({ width, height, data, href = null, onClick = () => {} }: { width: string | number; height: string | number; data: Lith[]; href?: string | null; onClick?: () => void }) {
const nestedData = nestItems(data);

return h("div.flex.row", [
h("div.example-container", [
h(LexHierarchyInner, { width, height, data: nestedData, href, onClick }),
]),
]);
}
75 changes: 75 additions & 0 deletions packages/lithology-hierarchy/src/lex-hierarchy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import hyper from "@macrostrat/hyper";
import styles from "./main.module.sass";
import { TreeNodeData } from "./nest-data";
import { LithologyTag } from "@macrostrat/data-components";
import React, {useMemo} from "react"

const h = hyper.styled(styles);

export default function LexHierarchyInner({ data, href, onClick }: { data: TreeNodeData; href: string; onClick: () => void }) {
return h(Tree, { data, level: 0, href, onClick });
}

const Tree = React.memo(function Tree({
data,
level = 0,
href,
onClick,
}: {
data: TreeNodeData;
level: number;
href: string;
onClick: () => void;
}) {
const headerEl = "h" + (level + 2);

const [subTrees, nodes] = useMemo(() => divideChildren(data), [data]);

return h("div.tree", { className: `tree-level-${level}` }, [
h("div.main-tree", [
h.if(data.children != null)(headerEl, capitalize(data.name)),
h.if(nodes.length > 0)(
"div.nodes",
nodes.map((d) =>
h("div.node", { key: d.name }, [
h(LithologyTag, {
data: d.lith ?? d,
href,
onClick,
}),
])
)
),
]),
subTrees.map((d) =>
h(Tree, {
key: d.name,
data: d,
level: level + 1,
href,
onClick,
})
),
]);
});


function capitalize(s: string) {
return s.charAt(0).toUpperCase() + s.slice(1);
}

function divideChildren(data: TreeNodeData) {
/** Divide children into terminal and non-terminal nodes */
const terminal = [];
const nonTerminal = [];
const { children = [] } = data;
for (const child of children) {
const len = child.children?.length ?? 0;
if (len == 0) {
terminal.push(child);
} else {
nonTerminal.push(child);
}
}
return [nonTerminal, terminal];
}
7 changes: 6 additions & 1 deletion packages/lithology-hierarchy/src/main.module.sass
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,9 @@
li
display: inline-block
margin-right: 0.5em
line-height: 2em
line-height: 2em

.nodes
display: flex
gap: .5em
flex-wrap: wrap
85 changes: 85 additions & 0 deletions packages/lithology-hierarchy/src/nest-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,88 @@ function convert(data: TreeNodeMap): TreeNodeData {
children: Array.from(data.children.values()).map(convert),
};
}


export function nestItems(liths: Lith[]): TreeNodeData {
const root: TreeNodeMap = { name: "Rocks", children: new Map() };
// Ensure that empty strings are treated as null
for (let lith of liths) {
for (const key of ["type", "group", "class"]) {
if (lith[key] === "") lith[key] = null;
}
}

for (let lith of liths) {
if (lith.class == null || lith.type == null)
console.error(lith, "Class and type should never be null");
if (lith.class == null) console.log(lith.name, "Class is null");
if (lith.type == null) console.log(lith.name, "Type is null");

// Create a class if it doesn't exist
if (lith.class != null) {
if (!root.children.has(lith.class)) {
root.children.set(lith.class, {
name: lith.class,
lith,
children: new Map<string, TreeNodeMap>(),
});
}
} else {
if (!root.children.has(lith.name)) {
root.children.set(lith.name, { name: lith.name, lith });
}
}

// Add the type to the class
if (lith.class != null && lith.type != null) {
const parent = root.children.get(lith.class);
if (!parent.children.has(lith.type)) {
parent.children.set(lith.type, {
name: lith.type,
children: new Map<string, TreeNodeMap>(),
lith,
});
}
}

// Add the group to the type
if (lith.class != null && lith.type != null) {
if (lith.group != null) {
const parent = root.children.get(lith.class);
const grandparent = parent.children.get(lith.type);
if (!grandparent.children.has(lith.group)) {
grandparent.children.set(lith.group, {
name: lith.group,
children: new Map<string, TreeNodeMap>(),
});
}
} else {
const parent = root.children.get(lith.class);
const grandparent = parent.children.get(lith.type);
if (!grandparent.children.has(lith.name)) {
grandparent.children.set(lith.name, { name: lith.name, lith });
}
}
}

// Add the lithology to the group
if (
lith.class != null &&
lith.type != null &&
lith.group != null &&
lith.name != null
) {
const parent = root.children.get(lith.class);
const grandparent = parent.children.get(lith.type);
const greatgrandparent = grandparent.children.get(lith.group);
greatgrandparent.children.set(lith.name, {
name: lith.name,
lith,
children: new Map<string, TreeNodeMap>(),
});
}
}

// Export to TreeNode format
return convert(root);
}
5 changes: 5 additions & 0 deletions pages/lex/+Page.client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,11 @@ export function Page() {
{ href: "/lex/fossils", title: "Fossils" },
"Fossil taxonomic occurrences from the Paleobiology Database linked to Macrostrat units"
),
h(
LinkCard,
{ href: "/lex/measurements", title: "Measurements" },
"Measurement names and descriptions"
),
h("p", [
h("strong", h("a", { href: "/sift" }, "Sift")),
", Macrostrat's legacy lexicon app, is still available for use as it is gradually brought into this new framework.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import { useState } from "react";
import { ContentPage } from "~/layouts";
import { useData } from "vike-react/useData";
import { SearchBar } from "~/components/general";
import { LithologyTag } from "~/components/lex/tag";
import { LexHierarchy } from "@macrostrat-web/lithology-hierarchy";
import { navigate } from "vike/client/router";

export function Page() {
const { res } = useData();
Expand All @@ -27,8 +28,6 @@ export function LexListPage({ res, title, route, id }) {
);
});

const grouped = groupByClassThenType(filtered);

return h(ContentPage, { className: "econ-list-page" }, [
h(StickyHeader, [
h(PageBreadcrumbs, { title }),
Expand All @@ -37,46 +36,6 @@ export function LexListPage({ res, title, route, id }) {
onChange: handleChange,
}),
]),
h(
"div.econ-list",
Object.entries(grouped).map(([className, types]) =>
h("div.econ-class-group", [
h("h2", UpperCase(className)),
...Object.entries(types).map(([type, group]) =>
h("div.econ-group", [
h("h3", UpperCase(type)),
h(
"div.econ-items",
group.map((d) => h(LithologyTag, { data: d, href: `/lex/${route}/${d[id]}` }))
),
])
),
])
)
),
h(LexHierarchy, { data: filtered, onClick: (e, item) => navigate(`/lex/${route}/${item[id]}`) }),
]);
}

function groupByClassThenType(items) {
return items.reduce((acc, item) => {
const { class: className, type } = item;

if (!type || type.trim() === "") {
return acc;
}

if (!acc[className]) {
acc[className] = {};
}
if (!acc[className][type]) {
acc[className][type] = [];
}

acc[className][type].push(item);
return acc;
}, {});
}

function UpperCase(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import h from "@macrostrat/hyper";
import { useData } from "vike-react/useData";
import { LexListPage } from "../economics/+Page";
import { LexListPage } from "../economics/+Page.client";

export function Page() {
const { res } = useData();
Expand Down
Loading