@indiscale/linkahead-webui-ext-map 0.5.0
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/.eslintrc.json +45 -0
- package/.gitlab-ci.yml +44 -0
- package/CHANGELOG.md +78 -0
- package/README.md +97 -0
- package/RELEASE_GUIDELINES.md +45 -0
- package/__mocks__/fileMock.js +3 -0
- package/__mocks__/styleMock.js +1 -0
- package/babel.config.js +22 -0
- package/cypress/e2e/standalone-map.cy.js +55 -0
- package/cypress/support/commands.js +25 -0
- package/cypress/support/e2e.js +17 -0
- package/cypress.config.js +10 -0
- package/dist/2b3e1faf89f94a483539.png +0 -0
- package/dist/416d91365b44e4b4f477.png +0 -0
- package/dist/8f2c4d11474275fbc161.png +0 -0
- package/dist/index.html +1 -0
- package/dist/linkahead-webui-ext-map.js +3 -0
- package/dist/linkahead-webui-ext-map.js.LICENSE.txt +45 -0
- package/dist/linkahead-webui-ext-map.js.map +1 -0
- package/iframe/index.html +6 -0
- package/indiscale-linkahead-webui-ext-map-0.4.1.tgz +0 -0
- package/jest.config.js +23 -0
- package/jest.setup.js +2 -0
- package/package.json +105 -0
- package/public/favicon.ico +0 -0
- package/public/index.html +11 -0
- package/public/logo192.png +0 -0
- package/public/logo512.png +0 -0
- package/public/manifest.json +25 -0
- package/public/map_tile_caosdb_logo.png +0 -0
- package/public/mock.js +41 -0
- package/public/robots.txt +3 -0
- package/select_query.json +3 -0
- package/src/AllMapEntities.tsx +294 -0
- package/src/CurrentPageEntities.js +318 -0
- package/src/Map.helpers.css +8 -0
- package/src/Map.helpers.js +536 -0
- package/src/Map.js +288 -0
- package/src/Map.test.js +252 -0
- package/src/MapConfig.js +75 -0
- package/src/__snapshots__/Map.test.js.snap +1725 -0
- package/src/components/Coordinates.js +24 -0
- package/src/components/ErrorComponent.tsx +2 -0
- package/src/components/Graticule.js +27 -0
- package/src/components/Loader.module.css +17 -0
- package/src/components/Loader.tsx +36 -0
- package/src/components/PathDropDown.js +108 -0
- package/src/components/SearchControl.js +502 -0
- package/src/components/ToggleMapButton.js +194 -0
- package/src/components/ViewChangeControl.js +104 -0
- package/src/constants/index.js +1 -0
- package/src/context/ConfigProvider.test.js +232 -0
- package/src/context/ConfigProvider.tsx +189 -0
- package/src/context/LoadingProvider.test.js +124 -0
- package/src/context/LoadingProvider.tsx +117 -0
- package/src/context/PathIdProvider.js +102 -0
- package/src/contrib/latlnggraticule/LICENSE +20 -0
- package/src/contrib/latlnggraticule/README.md +68 -0
- package/src/contrib/latlnggraticule/leaflet.latlng-graticule.js +528 -0
- package/src/contrib/simplegraticule/L.Graticule.js +138 -0
- package/src/default_config.json +57 -0
- package/src/global.d.ts +8 -0
- package/src/index.js +6 -0
- package/src/index.scss +133 -0
- package/src/logging.js +7 -0
- package/src/renderHtmlTemplate.test.js +60 -0
- package/src/select-search.min.svg +1 -0
- package/src/select-search.svg +46 -0
- package/src/setupTests.js +5 -0
- package/src/utils/GenerateQueryString.js +200 -0
- package/src/utils/GenerateQueryString.test.js +304 -0
- package/src/utils/index.ts +3 -0
- package/standalone.config.js +5 -0
- package/static/map_tile_caosdb_logo.png +0 -0
- package/tsconfig.json +25 -0
- package/webpack.config.js +193 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createContext,
|
|
3
|
+
useContext,
|
|
4
|
+
useMemo,
|
|
5
|
+
useRef,
|
|
6
|
+
useState,
|
|
7
|
+
useCallback,
|
|
8
|
+
type ReactNode,
|
|
9
|
+
} from "react";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* LoadingContext
|
|
13
|
+
* --------------
|
|
14
|
+
* Global loading state for the application.
|
|
15
|
+
*
|
|
16
|
+
* This context tracks whether *any* async operation is currently in progress.
|
|
17
|
+
* It supports multiple concurrent operations by keeping an internal counter.
|
|
18
|
+
*
|
|
19
|
+
* If at least one operation is running -> loading === true
|
|
20
|
+
* If all operations have finished -> loading === false
|
|
21
|
+
*/
|
|
22
|
+
type TLoadingContextValue = {
|
|
23
|
+
loading: boolean;
|
|
24
|
+
startLoading: () => void;
|
|
25
|
+
stopLoading: () => void;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const LoadingContext = createContext<TLoadingContextValue | null>(null);
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* LoadingProvider
|
|
32
|
+
* ---------------
|
|
33
|
+
* Provides a global loading state using a reference counter.
|
|
34
|
+
*
|
|
35
|
+
* Why a counter?
|
|
36
|
+
* - Multiple components may start async work at the same time
|
|
37
|
+
* - We only want to hide the spinner when *all* of them are finished
|
|
38
|
+
*
|
|
39
|
+
* Implementation details:
|
|
40
|
+
* - `inFlightRef` tracks how many operations are currently running
|
|
41
|
+
* - `loading` is derived from whether the counter is > 0
|
|
42
|
+
*
|
|
43
|
+
* This avoids race conditions and works correctly with React Strict Mode.
|
|
44
|
+
*/
|
|
45
|
+
type TLoadingProviderProps = {
|
|
46
|
+
children: ReactNode;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export const LoadingProvider = ({ children }: TLoadingProviderProps) => {
|
|
50
|
+
// Global loading state
|
|
51
|
+
const [loading, setLoading] = useState(false);
|
|
52
|
+
|
|
53
|
+
// Internal counter for active async operations
|
|
54
|
+
const inFlightRef = useRef(0);
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* startLoading
|
|
58
|
+
* ------------
|
|
59
|
+
* Call this when an async operation starts.
|
|
60
|
+
*
|
|
61
|
+
* Increments the counter and ensures `loading` is true.
|
|
62
|
+
*/
|
|
63
|
+
const startLoading = useCallback(() => {
|
|
64
|
+
inFlightRef.current += 1;
|
|
65
|
+
setLoading(true);
|
|
66
|
+
}, []);
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* stopLoading
|
|
70
|
+
* -----------
|
|
71
|
+
* Call this when an async operation finishes or is cancelled.
|
|
72
|
+
*
|
|
73
|
+
* Decrements the counter and only sets `loading` to false
|
|
74
|
+
* when *no* operations are left.
|
|
75
|
+
*/
|
|
76
|
+
const stopLoading = useCallback(() => {
|
|
77
|
+
inFlightRef.current = Math.max(0, inFlightRef.current - 1);
|
|
78
|
+
|
|
79
|
+
if (inFlightRef.current === 0) {
|
|
80
|
+
setLoading(false);
|
|
81
|
+
}
|
|
82
|
+
}, []);
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Memoized context value to avoid unnecessary re-renders.
|
|
86
|
+
*/
|
|
87
|
+
const value = useMemo<TLoadingContextValue>(
|
|
88
|
+
() => ({ loading, startLoading, stopLoading }),
|
|
89
|
+
[loading, startLoading, stopLoading]
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<LoadingContext.Provider value={value}>{children}</LoadingContext.Provider>
|
|
94
|
+
);
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* useLoading
|
|
99
|
+
* ----------
|
|
100
|
+
* Hook to access the global loading state.
|
|
101
|
+
*
|
|
102
|
+
* Must be used inside a <LoadingProvider>.
|
|
103
|
+
*
|
|
104
|
+
* Returns:
|
|
105
|
+
* - loading: boolean
|
|
106
|
+
* - startLoading(): void
|
|
107
|
+
* - stopLoading(): void
|
|
108
|
+
*/
|
|
109
|
+
export const useLoading = (): TLoadingContextValue => {
|
|
110
|
+
const ctx = useContext(LoadingContext);
|
|
111
|
+
|
|
112
|
+
if (!ctx) {
|
|
113
|
+
throw new Error("useLoading must be used within a <LoadingProvider>");
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return ctx;
|
|
117
|
+
};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { createContext, useContext, useMemo, useState } from "react";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* PathIdContext
|
|
5
|
+
* -------------
|
|
6
|
+
* Holds the currently selected path ID for the map.
|
|
7
|
+
*
|
|
8
|
+
* The path ID represents which logical path / filter is currently active
|
|
9
|
+
* (e.g. "same", a concrete path key, etc.).
|
|
10
|
+
*
|
|
11
|
+
* This value is shared across components via React Context.
|
|
12
|
+
*/
|
|
13
|
+
const PathIdContext = createContext(null);
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* get_last_path_id
|
|
17
|
+
* ----------------
|
|
18
|
+
* Reads the last selected path ID from sessionStorage.
|
|
19
|
+
*
|
|
20
|
+
* Why sessionStorage?
|
|
21
|
+
* - The selected path should persist across page reloads
|
|
22
|
+
* - But NOT across browser sessions (unlike localStorage)
|
|
23
|
+
* - Accessable in other parts of the App
|
|
24
|
+
*
|
|
25
|
+
* Edge case handling:
|
|
26
|
+
* - older code has written "undefined"
|
|
27
|
+
* into sessionStorage — we treat that as invalid and return null.
|
|
28
|
+
*
|
|
29
|
+
* @returns {string | null}
|
|
30
|
+
*/
|
|
31
|
+
const get_last_path_id = () => {
|
|
32
|
+
const old = sessionStorage.getItem("caosdb_map.display_path");
|
|
33
|
+
|
|
34
|
+
if (old === "undefined") {
|
|
35
|
+
// Defensive fallback for corrupted values
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return old;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* PathIdProvider
|
|
44
|
+
* --------------
|
|
45
|
+
* Provides the current path ID and a setter function to all descendants.
|
|
46
|
+
*
|
|
47
|
+
* Initialization logic:
|
|
48
|
+
* - If a previously selected path exists in sessionStorage, use it
|
|
49
|
+
* - Otherwise fall back to the special value `"same"`
|
|
50
|
+
*
|
|
51
|
+
* The context value is memoized to avoid unnecessary re-renders.
|
|
52
|
+
*/
|
|
53
|
+
export const PathIdProvider = ({ children }) => {
|
|
54
|
+
const [pathId, setPathId] = useState(() => get_last_path_id() ?? "same");
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Memoized context value:
|
|
58
|
+
* - `setPathId` is stable by React guarantees
|
|
59
|
+
* - `pathId` changes trigger updates where needed
|
|
60
|
+
*/
|
|
61
|
+
const value = useMemo(() => ({ pathId, setPathId }), [pathId]);
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<PathIdContext.Provider value={value}>{children}</PathIdContext.Provider>
|
|
65
|
+
);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Runtime prop validation.
|
|
70
|
+
*
|
|
71
|
+
* Ensures that PathIdProvider is always used with children.
|
|
72
|
+
*/
|
|
73
|
+
PathIdProvider.propTypes = {
|
|
74
|
+
children: (props, propName, componentName) => {
|
|
75
|
+
if (!props[propName]) {
|
|
76
|
+
return new Error(`${componentName} requires children`);
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* usePathId
|
|
84
|
+
* ---------
|
|
85
|
+
* Convenience hook to access the PathIdContext.
|
|
86
|
+
*
|
|
87
|
+
* Must be used inside a <PathIdProvider>.
|
|
88
|
+
*
|
|
89
|
+
* @returns {{
|
|
90
|
+
* pathId: string,
|
|
91
|
+
* setPathId: (value: string) => void
|
|
92
|
+
* }}
|
|
93
|
+
*/
|
|
94
|
+
export const usePathId = () => {
|
|
95
|
+
const ctx = useContext(PathIdContext);
|
|
96
|
+
|
|
97
|
+
if (!ctx) {
|
|
98
|
+
throw new Error("usePathId must be used within a <PathIdProvider>");
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return ctx;
|
|
102
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) JS Foundation and other contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
7
|
+
the Software without restriction, including without limitation the rights to
|
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
10
|
+
subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# leaflet.latlng-graticule
|
|
2
|
+
|
|
3
|
+
Create a Canvas as ImageOverlay to draw the Lat/Lon Graticule,
|
|
4
|
+
|
|
5
|
+
and show the grid tick label at the edges of the map.
|
|
6
|
+
|
|
7
|
+
Check out the [demo](https://cloudybay.github.io/leaflet.latlng-graticule/example/).
|
|
8
|
+
|
|
9
|
+
### Usage example
|
|
10
|
+
|
|
11
|
+
```javascript
|
|
12
|
+
L.latlngGraticule({
|
|
13
|
+
showLabel: true,
|
|
14
|
+
dashArray: [5, 5],
|
|
15
|
+
zoomInterval: [
|
|
16
|
+
{ start: 2, end: 3, interval: 30 },
|
|
17
|
+
{ start: 4, end: 4, interval: 10 },
|
|
18
|
+
{ start: 5, end: 7, interval: 5 },
|
|
19
|
+
{ start: 8, end: 10, interval: 1 },
|
|
20
|
+
],
|
|
21
|
+
}).addTo(map);
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Options
|
|
25
|
+
|
|
26
|
+
- **showLabel**: Show the grid tick label at the edges of the map. Default `true`
|
|
27
|
+
- **opacity**: Opacity of the Graticule and Label. Default `1`
|
|
28
|
+
- **weight**: The width of the graticule lines. Default `0.8`
|
|
29
|
+
- **color**: The color of the graticule lines. Default `#aaa`
|
|
30
|
+
- **font**: Font Style for the tick label. Default `12px Verdana`
|
|
31
|
+
- **fontColor**: Color of the tick label. Default `#aaa`
|
|
32
|
+
- **dashArray**: Used to achieve dashed lines. Default `[0,0]`
|
|
33
|
+
- **sides**: Used to name sides of the world. Default `['N', 'S', 'E', 'W']`
|
|
34
|
+
- **zoomInterval**: Use different intervals in different zoom levels. You can set for both latitude and longitude lines as the example, or set different intervals for latitude and longitude like below:
|
|
35
|
+
|
|
36
|
+
```javascript
|
|
37
|
+
zoomInterval: {
|
|
38
|
+
latitude: [
|
|
39
|
+
{start: 4, end: 6, interval: 5},
|
|
40
|
+
{start: 7, end: 20, interval: 1}
|
|
41
|
+
],
|
|
42
|
+
longitude: [
|
|
43
|
+
{start: 4, end: 6, interval: 10},
|
|
44
|
+
{start: 7, end: 20, interval: 2}
|
|
45
|
+
]
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
- **_Default_**:
|
|
50
|
+
|
|
51
|
+
```javascript
|
|
52
|
+
zoomInterval: [
|
|
53
|
+
{ start: 2, end: 2, interval: 40 },
|
|
54
|
+
{ start: 3, end: 3, interval: 20 },
|
|
55
|
+
{ start: 4, end: 4, interval: 10 },
|
|
56
|
+
{ start: 5, end: 7, interval: 5 },
|
|
57
|
+
{ start: 8, end: 20, interval: 1 },
|
|
58
|
+
];
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
#### Special Options
|
|
62
|
+
|
|
63
|
+
Some of the projections (like Lambert) is no straight line, set those options to draw a polyline graticule.
|
|
64
|
+
|
|
65
|
+
- **lngLineCurved**: Interval of polyline. Deafult `0`
|
|
66
|
+
- **latLineCurved**: Interval of polyline. Deafult `0`
|
|
67
|
+
|
|
68
|
+
Check out the [Lambert projection example](https://cloudybay.github.io/leaflet.latlng-graticule/example/lambert.html).
|