@macrostrat/map-interface 0.3.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/package.json +16 -12
- package/src/container.ts +66 -31
- package/src/context-panel/index.ts +2 -1
- package/src/context-panel/main.module.sass +7 -6
- package/src/controls.ts +12 -1
- package/src/dev/main.module.sass +1 -1
- package/src/expansion-panel/main.module.sass +8 -4
- package/src/helpers.ts +2 -2
- package/src/location-info/hash-string.ts +102 -0
- package/src/location-info/index.ts +6 -4
- package/src/location-info/utils.ts +0 -20
- package/src/location-panel/header.ts +19 -6
- package/src/location-panel/index.ts +32 -5
- package/src/location-panel/main.module.sass +3 -3
- package/src/main.module.sass +169 -82
- package/src/map-view/index.ts +6 -4
- package/src/map-view/terrain.ts +8 -5
- package/dist/index.cjs +0 -1783
- package/dist/index.cjs.map +0 -1
- package/dist/index.css +0 -918
- package/dist/index.css.map +0 -1
- package/dist/index.js +0 -1774
- package/dist/index.js.map +0 -1
- package/dist/types.d.ts +0 -235
- package/dist/types.d.ts.map +0 -1
- package/src/main.module.ref.styl +0 -407
- package/src/map-view/main.module.sass +0 -23
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
5
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [1.0.1] - 2024-10-02
|
|
8
|
+
|
|
9
|
+
- Bug fix: missing package specifier
|
|
10
|
+
|
|
11
|
+
## [1.0.0] - 2024-10-02
|
|
12
|
+
|
|
13
|
+
Updated to use BlueprintJS 5.0
|
package/package.json
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@macrostrat/map-interface",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Map interface for Macrostrat",
|
|
5
|
-
"main": "dist/
|
|
6
|
-
"module": "dist/
|
|
5
|
+
"main": "dist/main.js",
|
|
6
|
+
"module": "dist/module.js",
|
|
7
7
|
"types": "dist/types.d.ts",
|
|
8
8
|
"source": "src/index.ts",
|
|
9
|
-
"style": "dist/
|
|
9
|
+
"style": "dist/main.css",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@macrostrat/hyper": "^2.2.1",
|
|
12
|
-
"@macrostrat/mapbox-react": "
|
|
13
|
-
"@macrostrat/mapbox-utils": "
|
|
14
|
-
"@macrostrat/ui-components": "
|
|
12
|
+
"@macrostrat/mapbox-react": "2.2.1",
|
|
13
|
+
"@macrostrat/mapbox-utils": "1.3.0",
|
|
14
|
+
"@macrostrat/ui-components": "workspace:^4.1.0",
|
|
15
15
|
"@mapbox/tilebelt": "^1.0.2",
|
|
16
16
|
"@turf/bbox": "^6.5.0",
|
|
17
17
|
"chroma-js": "^2.4.2",
|
|
@@ -19,12 +19,13 @@
|
|
|
19
19
|
"d3-array": "^3.2.4",
|
|
20
20
|
"d3-format": "^3.1.0",
|
|
21
21
|
"mapbox-gl": "^2.15.0",
|
|
22
|
+
"query-string": "^8.1.0",
|
|
22
23
|
"transition-hook": "^1.5.2",
|
|
23
24
|
"underscore": "^1.13.6",
|
|
24
25
|
"use-resize-observer": "^9.1.0"
|
|
25
26
|
},
|
|
26
27
|
"peerDependencies": {
|
|
27
|
-
"@blueprintjs/core": "^3.43.0 || ^4.3.0",
|
|
28
|
+
"@blueprintjs/core": "^3.43.0 || ^4.3.0 || ^5.0.0",
|
|
28
29
|
"react": "^16.8.6||^17.0.0||^18.0.0",
|
|
29
30
|
"react-dom": "^16.8.6||^17.0.0||^18.0.0"
|
|
30
31
|
},
|
|
@@ -33,10 +34,10 @@
|
|
|
33
34
|
"build": "parcel build"
|
|
34
35
|
},
|
|
35
36
|
"exports": {
|
|
36
|
-
"typescript": "./src/index.ts",
|
|
37
37
|
".": {
|
|
38
|
-
"
|
|
39
|
-
"
|
|
38
|
+
"typescript": "./src",
|
|
39
|
+
"import": "./dist/module.js",
|
|
40
|
+
"require": "./dist/main.js"
|
|
40
41
|
},
|
|
41
42
|
"./dist/": {
|
|
42
43
|
"import": "./dist/",
|
|
@@ -46,5 +47,8 @@
|
|
|
46
47
|
"files": [
|
|
47
48
|
"dist",
|
|
48
49
|
"src"
|
|
49
|
-
]
|
|
50
|
+
],
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"parcel": "^2.12.0"
|
|
53
|
+
}
|
|
50
54
|
}
|
package/src/container.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import hyper from "@macrostrat/hyper";
|
|
1
|
+
import hyper, { addClassNames } from "@macrostrat/hyper";
|
|
2
2
|
import { HTMLDivProps } from "@blueprintjs/core";
|
|
3
3
|
import styles from "./main.module.sass";
|
|
4
4
|
import classNames from "classnames";
|
|
@@ -22,6 +22,16 @@ type AnyElement = React.ReactNode | React.ReactElement | React.ReactFragment;
|
|
|
22
22
|
export const PanelCard = (props) =>
|
|
23
23
|
h(Card, { ...props, className: classNames("panel-card", props.className) });
|
|
24
24
|
|
|
25
|
+
interface ContextStackProps extends HTMLDivProps {
|
|
26
|
+
adaptiveWidth: boolean;
|
|
27
|
+
navbar: AnyElement;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export enum DetailPanelStyle {
|
|
31
|
+
FIXED = "fixed",
|
|
32
|
+
FLOATING = "floating",
|
|
33
|
+
}
|
|
34
|
+
|
|
25
35
|
function _MapAreaContainer({
|
|
26
36
|
children,
|
|
27
37
|
className,
|
|
@@ -35,7 +45,10 @@ function _MapAreaContainer({
|
|
|
35
45
|
mapControls = h(MapBottomControls),
|
|
36
46
|
contextStackProps = null,
|
|
37
47
|
detailStackProps = null,
|
|
48
|
+
detailPanelStyle = DetailPanelStyle.FLOATING,
|
|
38
49
|
fitViewport = true,
|
|
50
|
+
showPanelOutlines = false,
|
|
51
|
+
preventMapInteraction = false,
|
|
39
52
|
...rest
|
|
40
53
|
}: {
|
|
41
54
|
navbar: AnyElement;
|
|
@@ -49,9 +62,11 @@ function _MapAreaContainer({
|
|
|
49
62
|
className?: string;
|
|
50
63
|
detailPanelOpen?: boolean;
|
|
51
64
|
contextPanelOpen?: boolean;
|
|
52
|
-
contextStackProps?:
|
|
65
|
+
contextStackProps?: ContextStackProps;
|
|
53
66
|
detailStackProps?: HTMLDivProps;
|
|
67
|
+
detailPanelStyle: DetailPanelStyle;
|
|
54
68
|
fitViewport?: boolean;
|
|
69
|
+
showPanelOutlines?: boolean;
|
|
55
70
|
}) {
|
|
56
71
|
const _detailPanelOpen = detailPanelOpen ?? detailPanel != null;
|
|
57
72
|
const contextPanelTrans = useTransition(contextPanelOpen, 800);
|
|
@@ -65,45 +80,65 @@ function _MapAreaContainer({
|
|
|
65
80
|
- These styles are doubly applied so we can have both namespaced and
|
|
66
81
|
outside-accessible styles for each case.
|
|
67
82
|
*/
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
},
|
|
83
|
+
const mainUIClassNames = classNames(
|
|
84
|
+
"map-container",
|
|
85
|
+
className,
|
|
86
|
+
`detail-panel-${detailPanelStyle}`,
|
|
73
87
|
`context-panel-${contextPanelTrans.stage}`,
|
|
74
88
|
`map-context-${contextPanelTrans.stage}`,
|
|
75
89
|
`detail-panel-${detailPanelTrans.stage}`,
|
|
76
|
-
`map-detail-${detailPanelTrans.stage}
|
|
90
|
+
`map-detail-${detailPanelTrans.stage}`,
|
|
91
|
+
{
|
|
92
|
+
"detail-panel-open": _detailPanelOpen,
|
|
93
|
+
"map-context-open": contextPanelOpen,
|
|
94
|
+
"show-panel-outlines": showPanelOutlines,
|
|
95
|
+
"fit-viewport": fitViewport,
|
|
96
|
+
}
|
|
77
97
|
);
|
|
78
98
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
99
|
+
const mapControlsExt = h([
|
|
100
|
+
h(ZoomControl, { className: "zoom-control" }),
|
|
101
|
+
h("div.spacer"),
|
|
102
|
+
mapControls,
|
|
103
|
+
]);
|
|
104
|
+
|
|
105
|
+
const detailStackExt = h(
|
|
106
|
+
"div.detail-stack.infodrawer-container",
|
|
107
|
+
detailStackProps,
|
|
86
108
|
[
|
|
87
|
-
h("div.
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
109
|
+
h("div.detail-panel-holder", null, detailPanel),
|
|
110
|
+
h.if(detailPanelStyle == DetailPanelStyle.FLOATING)([mapControlsExt]),
|
|
111
|
+
]
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
return h(MapStyledContainer, { className: mainUIClassNames }, [
|
|
115
|
+
h("div.main-row", [
|
|
116
|
+
h("div.map-ui", { ...rest }, [
|
|
117
|
+
h(ContextStack, { navbar, ...contextStackProps }, [
|
|
118
|
+
h.if(contextPanelTrans.shouldMount)([contextPanel]),
|
|
94
119
|
]),
|
|
95
120
|
//h(MapView),
|
|
96
121
|
children ?? mainPanel,
|
|
97
|
-
h(
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
]),
|
|
122
|
+
h.if(detailPanelStyle == DetailPanelStyle.FLOATING)([detailStackExt]),
|
|
123
|
+
h.if(detailPanelStyle == DetailPanelStyle.FIXED)(
|
|
124
|
+
"div.map-control-stack",
|
|
125
|
+
mapControlsExt
|
|
126
|
+
),
|
|
103
127
|
]),
|
|
104
|
-
h(
|
|
105
|
-
]
|
|
106
|
-
|
|
128
|
+
h.if(detailPanelStyle == DetailPanelStyle.FIXED)([detailStackExt]),
|
|
129
|
+
]),
|
|
130
|
+
h("div.bottom", null, bottomPanel),
|
|
131
|
+
]);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function ContextStack(props: ContextStackProps) {
|
|
135
|
+
const { adaptiveWidth, navbar, children, ...rest } = props;
|
|
136
|
+
const props1 = addClassNames(rest, { "adaptive-width": adaptiveWidth });
|
|
137
|
+
return h("div.context-stack", props1, [
|
|
138
|
+
navbar,
|
|
139
|
+
h("div.context-panel-holder", null, children),
|
|
140
|
+
h("div.spacer"),
|
|
141
|
+
]);
|
|
107
142
|
}
|
|
108
143
|
|
|
109
144
|
const MapProviders = ({ children }) =>
|
|
@@ -9,18 +9,19 @@
|
|
|
9
9
|
.searchbar
|
|
10
10
|
width: 100%
|
|
11
11
|
background-color: var(--panel-background-color)
|
|
12
|
-
border-radius:
|
|
12
|
+
border-radius: 5px
|
|
13
13
|
padding: 0 5px
|
|
14
14
|
display: flex
|
|
15
15
|
flex-direction: row
|
|
16
16
|
align-items: center
|
|
17
|
-
:
|
|
17
|
+
gap: 5px
|
|
18
|
+
:global(.bp5-input-group)
|
|
18
19
|
flex-grow: 1
|
|
19
20
|
cursor: text
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
|
|
22
|
+
:global(.bp5-navbar)>.loading-button
|
|
23
|
+
width: 40px
|
|
24
|
+
height: 40px
|
|
24
25
|
|
|
25
26
|
.status-tongue
|
|
26
27
|
background-color: var(--panel-background-color)
|
package/src/controls.ts
CHANGED
|
@@ -7,8 +7,10 @@ import {
|
|
|
7
7
|
GlobeControl,
|
|
8
8
|
ThreeDControl,
|
|
9
9
|
MapControlWrapper,
|
|
10
|
+
useMapStatus,
|
|
10
11
|
} from "@macrostrat/mapbox-react";
|
|
11
12
|
import { ScaleControl as BaseScaleControl } from "mapbox-gl";
|
|
13
|
+
import { DevToolsButtonSlot } from "@macrostrat/ui-components";
|
|
12
14
|
|
|
13
15
|
const h = hyper.styled(styles);
|
|
14
16
|
|
|
@@ -41,12 +43,21 @@ function GeolocationControl(props) {
|
|
|
41
43
|
});
|
|
42
44
|
}
|
|
43
45
|
|
|
44
|
-
export function MapBottomControls() {
|
|
46
|
+
export function MapBottomControls({ children }) {
|
|
47
|
+
const { isInitialized } = useMapStatus();
|
|
48
|
+
|
|
49
|
+
if (!isInitialized) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
45
53
|
return h("div.map-controls", [
|
|
46
54
|
h(ScaleControl),
|
|
47
55
|
h(ThreeDControl, { className: "map-3d-control" }),
|
|
48
56
|
h(CompassControl, { className: "compass-control" }),
|
|
49
57
|
h(GlobeControl, { className: "globe-control" }),
|
|
50
58
|
h(GeolocationControl, { className: "geolocation-control" }),
|
|
59
|
+
// If we have global development tools enabled, show the button
|
|
60
|
+
h(DevToolsButtonSlot, { className: "map-control" }),
|
|
61
|
+
children,
|
|
51
62
|
]);
|
|
52
63
|
}
|
package/src/dev/main.module.sass
CHANGED
|
@@ -20,9 +20,13 @@
|
|
|
20
20
|
.title
|
|
21
21
|
flex-grow: 1
|
|
22
22
|
|
|
23
|
-
// :global(.
|
|
23
|
+
// :global(.bp5-dark) .panel-subhead
|
|
24
24
|
// margin 0 1px
|
|
25
25
|
|
|
26
|
+
.info-panel-section
|
|
27
|
+
&>.panel-subhead
|
|
28
|
+
margin: -1px calc(var(--panel-padding-h) * -1) 0
|
|
29
|
+
|
|
26
30
|
.expansion-panel
|
|
27
31
|
padding: 0
|
|
28
32
|
flex-wrap: wrap
|
|
@@ -56,14 +60,14 @@
|
|
|
56
60
|
|
|
57
61
|
.expansion-summary-title-help
|
|
58
62
|
margin-left: 5px
|
|
59
|
-
:global(.
|
|
63
|
+
:global(.bp5-icon)
|
|
60
64
|
margin-left: 5px
|
|
61
65
|
|
|
62
66
|
.expansion-panel-header
|
|
63
67
|
cursor: pointer
|
|
64
68
|
&:hover
|
|
65
69
|
background-color: var(--accent-hover-color)
|
|
66
|
-
:global(.
|
|
70
|
+
:global(.bp5-icon)
|
|
67
71
|
transform: translate(0,3px)
|
|
68
72
|
|
|
69
73
|
.expansion-children
|
|
@@ -107,7 +111,7 @@
|
|
|
107
111
|
position: relative
|
|
108
112
|
|
|
109
113
|
.expandable-details-toggle
|
|
110
|
-
:global(.
|
|
114
|
+
:global(.bp5-button)
|
|
111
115
|
font-size: 10px
|
|
112
116
|
|
|
113
117
|
.expandable-details
|
package/src/helpers.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
useMapRef,
|
|
3
|
-
|
|
3
|
+
useMapEaseTo,
|
|
4
4
|
useMapDispatch,
|
|
5
5
|
useMapStatus,
|
|
6
6
|
} from "@macrostrat/mapbox-react";
|
|
@@ -67,7 +67,7 @@ export function MapPaddingManager({
|
|
|
67
67
|
});
|
|
68
68
|
|
|
69
69
|
// Ideally, we would not have to do this when we know the infobox is loaded
|
|
70
|
-
|
|
70
|
+
useMapEaseTo({ center: infoMarkerPosition, padding });
|
|
71
71
|
|
|
72
72
|
return null;
|
|
73
73
|
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import {
|
|
2
|
+
LatLng,
|
|
3
|
+
MapPosition,
|
|
4
|
+
formatCoordForZoomLevel,
|
|
5
|
+
} from "@macrostrat/mapbox-utils";
|
|
6
|
+
import { ParsedQuery } from "query-string";
|
|
7
|
+
import { fmt1, fmt2, fmtInt } from "./utils";
|
|
8
|
+
|
|
9
|
+
interface LocationHashParams {
|
|
10
|
+
x?: string;
|
|
11
|
+
y?: string;
|
|
12
|
+
z?: string;
|
|
13
|
+
a?: string;
|
|
14
|
+
e?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function applyMapPositionToHash(
|
|
18
|
+
args: LocationHashParams,
|
|
19
|
+
mapPosition: MapPosition | null
|
|
20
|
+
) {
|
|
21
|
+
const pos = mapPosition?.camera;
|
|
22
|
+
if (pos == null) return;
|
|
23
|
+
const zoom = mapPosition.target?.zoom;
|
|
24
|
+
|
|
25
|
+
args.x = formatCoordForZoomLevel(pos.lng, zoom);
|
|
26
|
+
args.y = formatCoordForZoomLevel(pos.lat, zoom);
|
|
27
|
+
|
|
28
|
+
if (pos.bearing == 0 && pos.pitch == 0 && zoom != null) {
|
|
29
|
+
args.z = fmt1(zoom);
|
|
30
|
+
} else if (pos.altitude != null) {
|
|
31
|
+
if (pos.altitude > 5000) {
|
|
32
|
+
args.z = fmt2(pos.altitude / 1000) + "km";
|
|
33
|
+
} else {
|
|
34
|
+
args.z = fmtInt(pos.altitude) + "m";
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if (pos.bearing != 0) {
|
|
38
|
+
let az = pos.bearing;
|
|
39
|
+
if (az < 0) az += 360;
|
|
40
|
+
args.a = fmtInt(az);
|
|
41
|
+
}
|
|
42
|
+
if (pos.pitch != 0) {
|
|
43
|
+
args.e = fmtInt(pos.pitch);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function _fmt(x: string | number | string[]) {
|
|
48
|
+
if (Array.isArray(x)) {
|
|
49
|
+
x = x[0];
|
|
50
|
+
}
|
|
51
|
+
return parseFloat(x.toString());
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function getMapPositionForHash(
|
|
55
|
+
hashData: ParsedQuery<string>,
|
|
56
|
+
centerPosition: LatLng | null
|
|
57
|
+
): MapPosition {
|
|
58
|
+
const {
|
|
59
|
+
x = centerPosition?.lng ?? 0,
|
|
60
|
+
y = centerPosition?.lat ?? 0,
|
|
61
|
+
// Different default for zoom depending on whether we have a marker
|
|
62
|
+
z = centerPosition != null ? 7 : 2,
|
|
63
|
+
a = 0,
|
|
64
|
+
e = 0,
|
|
65
|
+
} = hashData;
|
|
66
|
+
|
|
67
|
+
const lng = _fmt(x);
|
|
68
|
+
const lat = _fmt(y);
|
|
69
|
+
|
|
70
|
+
let altitude = null;
|
|
71
|
+
let zoom = null;
|
|
72
|
+
const _z = z.toString();
|
|
73
|
+
if (_z.endsWith("km")) {
|
|
74
|
+
altitude = _fmt(_z.substring(0, _z.length - 2)) * 1000;
|
|
75
|
+
} else if (_z.endsWith("m")) {
|
|
76
|
+
altitude = _fmt(_z.substring(0, _z.length - 1));
|
|
77
|
+
} else {
|
|
78
|
+
zoom = _fmt(z);
|
|
79
|
+
}
|
|
80
|
+
const bearing = _fmt(a);
|
|
81
|
+
const pitch = _fmt(e);
|
|
82
|
+
|
|
83
|
+
let target = undefined;
|
|
84
|
+
if (bearing == 0 && pitch == 0 && zoom != null) {
|
|
85
|
+
target = {
|
|
86
|
+
lat,
|
|
87
|
+
lng,
|
|
88
|
+
zoom,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
camera: {
|
|
94
|
+
lng: _fmt(x),
|
|
95
|
+
lat: _fmt(y),
|
|
96
|
+
altitude,
|
|
97
|
+
bearing: _fmt(a),
|
|
98
|
+
pitch: _fmt(e),
|
|
99
|
+
},
|
|
100
|
+
target,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import h from "@macrostrat/hyper";
|
|
2
2
|
import {
|
|
3
|
-
formatValue,
|
|
4
|
-
normalizeLng,
|
|
5
|
-
metersToFeet,
|
|
6
3
|
formatCoordForZoomLevel,
|
|
7
|
-
|
|
4
|
+
metersToFeet,
|
|
5
|
+
normalizeLng,
|
|
6
|
+
} from "@macrostrat/mapbox-utils";
|
|
7
|
+
import { formatValue } from "./utils";
|
|
8
|
+
|
|
9
|
+
export * from "./hash-string";
|
|
8
10
|
|
|
9
11
|
export function ValueWithUnit(props) {
|
|
10
12
|
const { value, unit } = props;
|
|
@@ -1,21 +1,5 @@
|
|
|
1
1
|
import { format } from "d3-format";
|
|
2
2
|
|
|
3
|
-
export function formatCoordForZoomLevel(val: number, zoom: number): string {
|
|
4
|
-
if (zoom < 2) {
|
|
5
|
-
return fmt1(val);
|
|
6
|
-
} else if (zoom < 4) {
|
|
7
|
-
return fmt2(val);
|
|
8
|
-
} else if (zoom < 7) {
|
|
9
|
-
return fmt3(val);
|
|
10
|
-
}
|
|
11
|
-
return fmt4(val);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function normalizeLng(lng) {
|
|
15
|
-
// via https://github.com/Leaflet/Leaflet/blob/32c9156cb1d1c9bd53130639ec4d8575fbeef5a6/src/core/Util.js#L87
|
|
16
|
-
return (((((lng - 180) % 360) + 360) % 360) - 180).toFixed(4);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
3
|
export const fmt4 = format(".4~f");
|
|
20
4
|
export const fmt3 = format(".3~f");
|
|
21
5
|
export const fmt2 = format(".2~f");
|
|
@@ -38,7 +22,3 @@ export function formatValue(val: number, precision: number = 0): string {
|
|
|
38
22
|
return fmt4(val);
|
|
39
23
|
}
|
|
40
24
|
}
|
|
41
|
-
|
|
42
|
-
export function metersToFeet(meters, precision = 0) {
|
|
43
|
-
return (meters * 3.28084).toFixed(precision);
|
|
44
|
-
}
|
|
@@ -11,12 +11,14 @@ import {
|
|
|
11
11
|
|
|
12
12
|
const h = hyper.styled(styles);
|
|
13
13
|
|
|
14
|
-
function PositionButton({ position }) {
|
|
14
|
+
function PositionButton({ position, showCopyLink = false }) {
|
|
15
15
|
const focusState = useFocusState(position);
|
|
16
16
|
|
|
17
|
+
const copyLinkIsVisible = isCentered(focusState) && showCopyLink;
|
|
18
|
+
|
|
17
19
|
return h("div.position-controls", [
|
|
18
20
|
h(LocationFocusButton, { location: position, focusState }, []),
|
|
19
|
-
|
|
21
|
+
h.if(copyLinkIsVisible)(CopyLinkButton, { itemName: "position" }),
|
|
20
22
|
]);
|
|
21
23
|
}
|
|
22
24
|
|
|
@@ -63,20 +65,31 @@ function CopyLinkButton({ itemName, children, onClick, ...rest }) {
|
|
|
63
65
|
);
|
|
64
66
|
}
|
|
65
67
|
|
|
66
|
-
interface InfoDrawerHeaderProps {
|
|
68
|
+
export interface InfoDrawerHeaderProps {
|
|
67
69
|
onClose: () => void;
|
|
68
70
|
position: mapboxgl.LngLat;
|
|
69
71
|
zoom?: number;
|
|
70
72
|
elevation?: number;
|
|
73
|
+
showCopyPositionButton?: boolean;
|
|
71
74
|
}
|
|
72
75
|
|
|
73
76
|
export function InfoDrawerHeader(props: InfoDrawerHeaderProps) {
|
|
74
|
-
const {
|
|
77
|
+
const {
|
|
78
|
+
onClose,
|
|
79
|
+
position,
|
|
80
|
+
zoom = 7,
|
|
81
|
+
elevation,
|
|
82
|
+
showCopyPositionButton,
|
|
83
|
+
} = props;
|
|
75
84
|
|
|
76
85
|
return h("header.location-panel-header", [
|
|
77
|
-
h(PositionButton, { position }),
|
|
86
|
+
h(PositionButton, { position, showCopyLink: showCopyPositionButton }),
|
|
78
87
|
h("div.spacer"),
|
|
79
|
-
h(LngLatCoords, {
|
|
88
|
+
h(LngLatCoords, {
|
|
89
|
+
position,
|
|
90
|
+
zoom,
|
|
91
|
+
className: "infodrawer-header-item",
|
|
92
|
+
}),
|
|
80
93
|
h.if(elevation != null)(Elevation, {
|
|
81
94
|
elevation,
|
|
82
95
|
className: "infodrawer-header-item",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Card } from "@blueprintjs/core";
|
|
2
2
|
import hyper from "@macrostrat/hyper";
|
|
3
|
-
import { InfoDrawerHeader } from "./header";
|
|
3
|
+
import { InfoDrawerHeader, InfoDrawerHeaderProps } from "./header";
|
|
4
4
|
import classNames from "classnames";
|
|
5
5
|
import styles from "./main.module.sass";
|
|
6
6
|
import { ErrorBoundary } from "@macrostrat/ui-components";
|
|
@@ -12,11 +12,38 @@ export function InfoDrawerContainer(props) {
|
|
|
12
12
|
return h(Card, { ...props, className });
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
interface BaseInfoDrawerProps extends InfoDrawerHeaderProps {
|
|
16
|
+
className?: string;
|
|
17
|
+
title?: string;
|
|
18
|
+
headerElement?: JSX.Element;
|
|
19
|
+
children?: React.ReactNode;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function BaseInfoDrawer(props: BaseInfoDrawerProps) {
|
|
23
|
+
const {
|
|
24
|
+
className,
|
|
25
|
+
headerElement = null,
|
|
26
|
+
title,
|
|
27
|
+
onClose,
|
|
28
|
+
children,
|
|
29
|
+
...rest
|
|
30
|
+
} = props;
|
|
31
|
+
const header =
|
|
32
|
+
headerElement ??
|
|
33
|
+
h(InfoDrawerHeader, { onClose, ...rest }, [
|
|
34
|
+
title == null ? null : h("h3", [title]),
|
|
35
|
+
]);
|
|
36
|
+
return h(InfoDrawerContainer, { className }, [
|
|
37
|
+
header,
|
|
38
|
+
h(
|
|
39
|
+
"div.infodrawer-body",
|
|
40
|
+
h("div.infodrawer-contents", h(ErrorBoundary, null, children))
|
|
41
|
+
),
|
|
42
|
+
]);
|
|
43
|
+
}
|
|
44
|
+
|
|
15
45
|
export function LocationPanel(props) {
|
|
16
46
|
const { children, className, loading = false, ...rest } = props;
|
|
17
47
|
const cls = classNames("location-panel", className, { loading });
|
|
18
|
-
return h(
|
|
19
|
-
h(InfoDrawerHeader, rest),
|
|
20
|
-
h("div.infodrawer-body", h("div.infodrawer-contents", h(ErrorBoundary, null, children))),
|
|
21
|
-
]);
|
|
48
|
+
return h(BaseInfoDrawer, { className: cls, ...rest }, children);
|
|
22
49
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
.copy-link-button:global(.
|
|
1
|
+
.copy-link-button:global(.bp5-minimal.bp5-button)
|
|
2
2
|
color: var(--text-subtle-color)
|
|
3
3
|
svg
|
|
4
4
|
fill: var(--text-subtle-color)
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
.left-icon
|
|
16
16
|
padding: 7px
|
|
17
17
|
|
|
18
|
-
.position-controls :global(.
|
|
18
|
+
.position-controls :global(.bp5-button)
|
|
19
19
|
font-size: 12px !important
|
|
20
20
|
|
|
21
21
|
.infodrawer-header-item
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
display: flex
|
|
33
33
|
flex-direction: column
|
|
34
34
|
overflow: hidden
|
|
35
|
-
&:global(.
|
|
35
|
+
&:global(.bp5-card)
|
|
36
36
|
padding: 0
|
|
37
37
|
|
|
38
38
|
&.loading
|