Skip to content

Commit 1bd9d05

Browse files
authored
Merge pull request #135 from UW-Macrostrat/map-panel
Enhanced details panel
2 parents 91441e2 + 0eafc94 commit 1bd9d05

27 files changed

+2085
-1
lines changed

packages/map-interface/CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file. The format
44
is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this
55
project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [1.4.1] - 2025-07-14
8+
9+
Added map-panel component
10+
11+
Need to
12+
13+
- integrate fossils
14+
715
## [1.4.0] - 2025-07-02
816

917
- Allow `DevMapPage` to set more options for the map, inherited from Mapbox GL

packages/map-interface/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@macrostrat/map-interface",
3-
"version": "1.4.0",
3+
"version": "1.4.1",
44
"description": "Map interface for Macrostrat",
55
"main": "dist/cjs/index.js",
66
"module": "dist/esm/index.js",
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.list-button
2+
display: flex
3+
margin-bottom: 3px
4+
padding: 0px 8px !important
5+
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { hyperStyled } from "@macrostrat/hyper";
2+
import { Button, ButtonProps, IconName } from "@blueprintjs/core";
3+
import styles from "./buttons.module.styl";
4+
5+
const h = hyperStyled(styles);
6+
7+
export const LinkButton = (props: ButtonProps & { to: string }) => {
8+
const { to, ...rest } = props;
9+
// Check if in react-router context
10+
let onClick = () => {};
11+
try {
12+
onClick = useHashNavigate(to);
13+
} catch (err) {
14+
console.warn("LinkButton used outside of react-router context");
15+
}
16+
17+
return h(Button, {
18+
...rest,
19+
onClick,
20+
});
21+
};
22+
23+
type ListButtonProps = ButtonProps & {
24+
icon: React.ComponentType | IconName | React.ReactNode;
25+
};
26+
27+
function isIconName(x: ListButtonProps["icon"]): x is IconName {
28+
return typeof x == "string";
29+
}
30+
31+
export const ListButton = (props: ListButtonProps) => {
32+
let { icon, ...rest } = props;
33+
if (!isIconName(props.icon)) {
34+
icon = h(props.icon, { size: 20 });
35+
}
36+
return h(Button, { ...rest, className: "list-button", icon });
37+
};
38+
39+
type LayerButtonProps = ListButtonProps & {
40+
layer: MapLayer;
41+
name: string;
42+
buttonComponent?: React.ComponentType<ListButtonProps>;
43+
};
44+
45+
export function LayerButton(props: LayerButtonProps) {
46+
const { buttonComponent = ListButton, layer, name, ...rest } = props;
47+
const active = useAppState((state) => state.core.mapLayers.has(layer));
48+
const runAction = useAppActions();
49+
const onClick = () => runAction({ type: "toggle-map-layer", layer });
50+
return h(buttonComponent, {
51+
active,
52+
onClick,
53+
text: name,
54+
...rest,
55+
});
56+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
.card-header-left button
2+
margin-right 5px
3+
4+
.card-content
5+
h2:first-child
6+
margin-top 0
7+
overflow-y scroll
8+
padding 10px
9+
min-height 0
10+
11+
12+
.spacer
13+
flex-grow 1
14+
pointer-events none
15+
16+
.closeable-card
17+
padding 0
18+
min-height 40px
19+
flex-shrink 1
20+
display flex
21+
flex-direction: column
22+
.card-header, .card-header-left
23+
display: flex
24+
align-items: center
25+
.spacer
26+
flex-grow 0.01
27+
.card-header-left
28+
justify-content: flex-start
29+
flex 1
30+
min-width: 0
31+
overflow-x: hidden
32+
.card-header
33+
justify-content space-between
34+
padding 5px
35+
border-bottom 1px solid var(--panel-rule-color)
36+
height 40px
37+
38+
39+
@media screen and (max-width: 768px) {
40+
.closeable-card {
41+
border-radius: 0px;
42+
}
43+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { Card, Button } from "@blueprintjs/core";
2+
import hyper from "@macrostrat/hyper";
3+
import classNames from "classnames";
4+
import styles from "./card.module.styl";
5+
6+
const h = hyper.styled(styles);
7+
8+
const CloseableCardHeader = (props) => h("div.card-header-left", props);
9+
10+
const CloseableCard = (props) => {
11+
let {
12+
isOpen = true,
13+
onClose,
14+
title,
15+
transitionDuration,
16+
showHeader = true,
17+
insetContent = true,
18+
renderHeader,
19+
children,
20+
className,
21+
...rest
22+
} = props;
23+
if (!isOpen) {
24+
return null;
25+
}
26+
rest.className = classNames("closeable-card", className);
27+
28+
// Set header from "CloseableCardHeader" unless not set,
29+
// otherwise use "title"
30+
let header = null;
31+
if (renderHeader != null) {
32+
header = renderHeader();
33+
}
34+
35+
if (header == null && title != null) {
36+
if (title != null) {
37+
title = h("h4", title);
38+
}
39+
header = h([title]);
40+
}
41+
42+
return h(Card, rest, [
43+
h.if(showHeader)("div.card-header", [
44+
header,
45+
//h("div.spacer"),
46+
h(Button, {
47+
icon: "small-cross",
48+
className: "card-close-button",
49+
minimal: true,
50+
"aria-label": "Close",
51+
onClick: onClose,
52+
}),
53+
]),
54+
h(
55+
"div.card-content",
56+
{ className: classNames({ inset: insetContent }) },
57+
children,
58+
),
59+
]);
60+
};
61+
62+
CloseableCard.Header = CloseableCardHeader;
63+
64+
export { CloseableCard };
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
.documentation-figure
2+
position relative
3+
display: flex
4+
flex-direction: column
5+
clear: both
6+
margin: 0.5em auto 1em
7+
&.right
8+
max-width: 50%
9+
float: right
10+
margin 0.5em 0em 1em 1em
11+
12+
&.left
13+
max-width: 50%
14+
float: left
15+
margin 0.5em 1em 1em 0
16+
17+
&>img, &>video
18+
float: none
19+
width: 100%
20+
margin: 0
21+
max-width: 100%
22+
figcaption
23+
text-align center
24+
font-size 0.8em
25+
margin-top: 0.25em
26+
font-style italic
27+
color var(--secondary-text-color)
28+
29+
.documentation-video-standalone
30+
max-width: 50%
31+
float: left
32+
margin 0.5em 1em 1em 0
33+
34+
.dev-tag
35+
font-size 0.8em
36+
font-weight 400
37+
padding: 0.25em 0.5em
38+
margin 0.2em 0.5em 0
39+
border-radius: 4px
40+
font-style italic
41+
float right
42+
background-color: var(--accent-secondary-color)
43+
color: var(--text-subtle-color)
44+
45+
:global
46+
47+
.text-panel
48+
p, ul, ol
49+
hyphens: auto
50+
img, video
51+
border-radius: 6px
52+
box-shadow: 0 0 6px var(--card-shadow-color)
53+
code
54+
font-size: 0.9em
55+
56+
.text-panel
57+
margin 0em 0.5em
58+
h2, h3, h4
59+
// Make sure headings clear floats (for videos)
60+
clear: both
61+
display: inline-block
62+
width: 100%
63+
margin 1em 0 0.5em
64+
ul
65+
margin-top 0.2em
66+
67+
.new-swatch
68+
font-size 0.8em
69+
font-weight 400
70+
display inline-block
71+
padding: 0.25em 0.5em
72+
margin 0 0.5em
73+
border-radius: 4px
74+
font-style italic
75+
float right
76+
background-color: var(--ui-color-accent)
77+
color: var(--ui-color-accent-text)
78+
79+
h2.version .version-name
80+
background-color: var(--ui-color-accent)
81+
border-radius: 4px
82+
83+
.version
84+
.version-name
85+
display inline-block
86+
padding: 0.25em 0.5em
87+
margin-right 0.5em
88+
color: var(--ui-color-accent-text)
89+
code
90+
font-size 0.9em
91+
font-weight: 400
92+
.date
93+
font-style italic
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import { Alignment } from "@blueprintjs/core";
2+
import { routerBasename } from "@macrostrat-web/settings";
3+
import hyper from "@macrostrat/hyper";
4+
import classNames from "classnames";
5+
import { useInView } from "react-intersection-observer";
6+
import { HashLink } from "react-router-hash-link";
7+
import { joinURL } from "@macrostrat/ui-components";
8+
import styles from "./docs.module.styl";
9+
10+
const h = hyper.styled(styles);
11+
12+
const urlBase = "https://macrostrat-media.s3.amazonaws.com/maps/docs/";
13+
14+
function DocsMediaFile({ href, lazy = true, className }) {
15+
const { ref, inView } = useInView({ triggerOnce: true });
16+
let src = null;
17+
if (inView || !lazy) {
18+
src = joinURL(urlBase, href);
19+
}
20+
if (href.endsWith(".mp4")) {
21+
return h("video", {
22+
ref,
23+
autoPlay: true,
24+
loop: true,
25+
playsInline: true,
26+
muted: true,
27+
type: "video/mp4",
28+
src,
29+
className,
30+
});
31+
}
32+
return h("img", {
33+
ref,
34+
src,
35+
className,
36+
});
37+
}
38+
39+
export function DocsMedia({
40+
children,
41+
width,
42+
align = Alignment.RIGHT,
43+
...rest
44+
}) {
45+
const className = classNames(align, {
46+
captioned: children != null,
47+
});
48+
return h("figure.documentation-figure", { style: { width }, className }, [
49+
h(DocsMediaFile, rest),
50+
h.if(children != null)("figcaption.caption", children),
51+
]);
52+
}
53+
54+
export function DocsVideo({ slug, lazy = true, className }) {
55+
// For legacy reasons, the alignment is set to left
56+
return h(DocsMedia, {
57+
href: slug + ".mp4",
58+
lazy,
59+
className,
60+
align: Alignment.LEFT,
61+
});
62+
}
63+
64+
export function InternalLink({ to, children }) {
65+
// We'd use a link component, but it doesn't properly navigate to the hash state
66+
return h(
67+
"a.internal-link",
68+
{
69+
href: joinURL(routerBasename, to),
70+
},
71+
children,
72+
);
73+
}
74+
75+
export function NewSwatch({ children, version = 0 }) {
76+
return h(
77+
HashLink,
78+
{
79+
to: routerBasename + `changelog#version-${version}`,
80+
className: "new-swatch",
81+
},
82+
children,
83+
);
84+
}
85+
86+
export function Version({ spec, date, major = true, dev = false }) {
87+
return h(`h${major ? 2 : 3}.version`, { id: `version-${spec}` }, [
88+
h("span.version-name", ["Version ", h("code", spec)]),
89+
h("span.date", date),
90+
h.if(dev)("span.dev-tag", "dev"),
91+
]);
92+
}

0 commit comments

Comments
 (0)