├── .changeset ├── README.md └── config.json ├── .editorconfig ├── .eslintignore ├── .eslintrc.json ├── .github └── workflows │ └── test.yml ├── .gitignore ├── .npmignore ├── .prettierignore ├── .prettierrc ├── CHANGELOG.md ├── LICENSE ├── README.md ├── babel.config.js ├── example ├── data │ ├── italy-regions.json │ └── usa-cities.json ├── index.html ├── package.json ├── src │ ├── components │ │ └── SeeCodeButton.tsx │ ├── examples │ │ ├── Annotator.tsx │ │ ├── Intro.tsx │ │ ├── RegionsExplorer.tsx │ │ ├── Spring.tsx │ │ └── experiment.tsx │ ├── index.tsx │ ├── ol-components │ │ └── DarkCanvasLayer.tsx │ └── style.css ├── tsconfig.json ├── vercel.json └── yarn.lock ├── jest.config.js ├── package.json ├── rollup.config.js ├── src ├── MapComponent.tsx ├── index.ts ├── ol-types.tsx ├── reconciler.ts ├── spring.ts └── utils.ts ├── tests ├── attachments.test.tsx ├── props.test.tsx ├── utils.test.ts └── utils.tsx ├── tsconfig.json └── yarn.lock /.changeset/README.md: -------------------------------------------------------------------------------- 1 | # Changesets 2 | 3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works 4 | with multi-package repos, or single-package repos to help you version and publish your code. You can 5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets) 6 | 7 | We have a quick list of common questions to get you started engaging with this project in 8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) 9 | -------------------------------------------------------------------------------- /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@1.6.0/schema.json", 3 | "changelog": "@changesets/changelog-git", 4 | "commit": true, 5 | "linked": [], 6 | "access": "public", 7 | "baseBranch": "main", 8 | "updateInternalDependencies": "minor", 9 | "ignore": [] 10 | } 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | end_of_line = lf 3 | insert_final_newline = true 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | node_modules/ 3 | .yarn/ -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true, 5 | "node": true 6 | }, 7 | "extends": ["prettier", "plugin:prettier/recommended", "plugin:react-hooks/recommended", "plugin:import/errors", "plugin:import/warnings"], 8 | "plugins": ["@typescript-eslint", "react", "react-hooks", "import", "jest", "prettier"], 9 | "parser": "@typescript-eslint/parser", 10 | "parserOptions": { 11 | "ecmaFeatures": { 12 | "jsx": true 13 | }, 14 | "ecmaVersion": 2018, 15 | "sourceType": "module", 16 | "rules": { 17 | "curly": ["warn", "multi-line", "consistent"], 18 | "no-console": "off", 19 | "no-empty-pattern": "warn", 20 | "no-duplicate-imports": "error", 21 | "import/no-unresolved": "off", 22 | "import/export": "error", 23 | // https://github.com/typescript-eslint/typescript-eslint/blob/master/docs/getting-started/linting/FAQ.md#eslint-plugin-import 24 | // We recommend you do not use the following import/* rules, as TypeScript provides the same checks as part of standard type checking: 25 | "import/named": "off", 26 | "import/namespace": "off", 27 | "import/default": "off", 28 | "no-unused-vars": ["warn", { "argsIgnorePattern": "^_", "varsIgnorePattern": "^_" }], 29 | "@typescript-eslint/no-unused-vars": ["warn", { "argsIgnorePattern": "^_", "varsIgnorePattern": "^_" }], 30 | "@typescript-eslint/no-use-before-define": "off", 31 | "@typescript-eslint/no-empty-function": "off", 32 | "@typescript-eslint/no-empty-interface": "off", 33 | "@typescript-eslint/no-explicit-any": "off", 34 | "jest/consistent-test-it": ["error", { "fn": "it", "withinDescribe": "it" }] 35 | } 36 | }, 37 | "rules": { 38 | "prettier/prettier": [ 39 | "warn", 40 | { 41 | "endOfLine": "auto", 42 | "semi": true, 43 | "singleQuote": true, 44 | "arrowParens": "avoid", 45 | "jsxSingleQuote": true, 46 | "printWidth": 160, 47 | "tabWidth": 2, 48 | "trailingComma": "all" 49 | } 50 | ] 51 | }, 52 | "settings": { 53 | "react": { 54 | "version": "detect" 55 | }, 56 | "import/extensions": [".js", ".jsx", ".ts", ".tsx"], 57 | "import/parsers": { 58 | "@typescript-eslint/parser": [".js", ".jsx", ".ts", ".tsx"] 59 | }, 60 | "import/resolver": { 61 | "node": { 62 | "extensions": [".js", ".jsx", ".ts", ".tsx", ".json"], 63 | "paths": ["src"] 64 | }, 65 | "alias": { 66 | "extensions": [".js", ".jsx", ".ts", ".tsx", ".json"], 67 | "map": [["@react-three/fiber", "./packages/fiber/src/web"]] 68 | } 69 | } 70 | }, 71 | "overrides": [ 72 | { 73 | "files": ["src"], 74 | "parserOptions": { 75 | "project": "./tsconfig.json" 76 | } 77 | } 78 | ] 79 | } 80 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | on: 3 | push: 4 | branches: 5 | - 'main' 6 | pull_request: {} 7 | jobs: 8 | build: 9 | name: Build, lint, and test 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout repo 14 | uses: actions/checkout@v2 15 | 16 | - name: Use Node ${{ matrix.node }} 17 | uses: actions/setup-node@v1 18 | with: 19 | node-version: ${{ matrix.node }} 20 | 21 | - name: Install deps and build (with cache) 22 | uses: bahmutov/npm-install@v1 23 | with: 24 | install-command: yarn --immutable --silent 25 | 26 | - name: Check types 27 | run: yarn typecheck 28 | 29 | - name: Build 30 | run: yarn build 31 | 32 | - name: Jest run 33 | run: yarn test 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | coverage/ 3 | dist/ 4 | build/ 5 | types/ 6 | Thumbs.db 7 | ehthumbs.db 8 | Desktop.ini 9 | $RECYCLE.BIN/ 10 | .DS_Store 11 | .vscode 12 | .docz/ 13 | package-lock.json 14 | coverage/ 15 | .idea 16 | yarn-error.log 17 | .size-snapshot.json 18 | __tests__/__image_snapshots__/__diff_output__ 19 | .parcel-cache 20 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | examples/ 2 | example/ 3 | .codesandbox/ 4 | .github/ 5 | .husky/ 6 | markdown/ 7 | src/ 8 | tests/ 9 | coverage/ -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | .nyc_output 7 | coverage 8 | storybook-static 9 | tsconfig.json 10 | **/data/* 11 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "endOfLine": "auto", 3 | "semi": true, 4 | "singleQuote": true, 5 | "arrowParens": "avoid", 6 | "jsxSingleQuote": true, 7 | "printWidth": 160, 8 | "tabWidth": 2, 9 | "trailingComma": "all", 10 | "overrides": [ 11 | { 12 | "files": "*.html", 13 | "options": { 14 | "singleQuote": false 15 | } 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # react-ol-fiber 2 | 3 | ## 3.0.2 4 | 5 | ### Patch Changes 6 | 7 | - 3a33a56: Fixed several bugs in element attaching in reconciler 8 | 9 | ## 3.0.1 10 | 11 | ### Patch Changes 12 | 13 | - e47bc82: Fixes a bug where immutable child are appended in the wrong order after a rerender 14 | 15 | ## 3.0.0 16 | 17 | ### Major Changes 18 | 19 | - 32602d9: Renamed primitive to olPrimitive 20 | 21 | ### Minor Changes 22 | 23 | - d47967f: Added (very hacky) support for render functions using `` component 24 | 25 | ## 2.2.2 26 | 27 | ### Patch Changes 28 | 29 | - 8a03938: Fixed a bug when using Supense inside the reconciler 30 | 31 | ## 2.2.1 32 | 33 | ### Patch Changes 34 | 35 | - 6046e9c: Hack for instance recreation with spring applyProps 36 | 37 | ## 2.2.0 38 | 39 | ### Minor Changes 40 | 41 | - 60bb479: Added react-spring support 42 | 43 | ## 2.1.0 44 | 45 | ### Minor Changes 46 | 47 | - f98e503: Automatically call changed if available 48 | 49 | ## 2.0.0 50 | 51 | ### Major Changes 52 | 53 | - 60196f6: BREAKING: Splitted args for native elements into args and arg. While args now takes always an array, arg can take the first object parameter. 54 | 55 | ## 1.1.1 56 | 57 | ### Patch Changes 58 | 59 | - 1037b49: Updated internal deps 60 | 61 | ## 1.1.0 62 | 63 | ### Minor Changes 64 | 65 | - 8ba0424: Added a `` wrapper for primitives to maintain the correct type using generics 66 | 67 | ### Patch Changes 68 | 69 | - 64437d8: Fixed correct target type in event handlers, now it will have the correct type for all events 70 | 71 | ## 1.0.0 72 | 73 | ### Major Changes 74 | 75 | - b56bf79: Manual typings for some event handlers. Now using any cast on the event parameter is not necessary anymore. 76 | 77 | ## 0.4.0 78 | 79 | ### Minor Changes 80 | 81 | - 90c5337: Added support for controls: 82 | 83 | ``` 84 | 85 | 86 | 87 | 88 | ``` 89 | 90 | ## 0.3.2 91 | 92 | ### Patch Changes 93 | 94 | - 30dc50d: Updated dependencies 95 | 96 | ## 0.3.1 97 | 98 | ### Patch Changes 99 | 100 | - 6615d99: Removed useless files from NPM 101 | 102 | ## 0.3.0 103 | 104 | ### Minor Changes 105 | 106 | - d9a333f: Fixed JSX intrisics clashing for ``, now it's callable using `` 107 | 108 | ## 0.2.0 109 | 110 | ### Minor Changes 111 | 112 | - d194b16: Allow attachAdd to use setters 113 | - cb62a44: Detect attach automatically in primitives 114 | 115 | ### Patch Changes 116 | 117 | - 8a4634b: Fixed getImmutableChildren with components and attach prop priority 118 | 119 | ## 0.1.0 120 | 121 | ### Minor Changes 122 | 123 | - f17dc72: Support for immutable objects re-creation 124 | 125 | ## 0.0.5 126 | 127 | ### Patch Changes 128 | 129 | - Fixed style attach prop behaviour 130 | 131 | ## 0.0.4 132 | 133 | ### Patch Changes 134 | 135 | - Add support for styles 136 | 137 | ## 0.0.3 138 | 139 | ### Patch Changes 140 | 141 | - Add support for geometries 142 | - Now features doesn't need the attach prop anymore 143 | 144 | ## 0.0.2 145 | 146 | ### Patch Changes 147 | 148 | - Reduced package size. 149 | 150 | ## 0.0.1 151 | 152 | ### Major Changes 153 | 154 | - Initial alpha publish to npm. 155 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Giulio Zausa 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, 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, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-ol-fiber 2 | 3 | [](https://npmjs.com/package/react-ol-fiber) 4 | [](https://npmjs.com/package/react-ol-fiber) 5 | [](https://github.com/giulioz/react-ol-fiber/actions/workflows/test.yml) 6 | 7 | react-ol-fiber is a React renderer for OpenLayers. 8 | 9 | Build your maps declaratively with re-usable, self-contained components that react to state, are readily interactive and can participate in React's ecosystem. 10 | 11 | ```bash 12 | npm install ol react-ol-fiber 13 | ``` 14 | 15 | Being a renderer and not a wrapper it's not tied to a specific version of OpenLayers, and allows easy extensibility. 16 | 17 | ## Quick Start Code 18 | 19 | [](https://codesandbox.io/s/react-ol-fiber-qs-32s5j?fontsize=14&hidenavigation=1&theme=dark&view=preview) 20 | 21 | 22 | 23 | ```tsx 24 | import React, { useEffect, useState } from 'react'; 25 | import ReactDOM from 'react-dom'; 26 | import { MapComponent } from 'react-ol-fiber'; 27 | import 'ol/ol.css'; 28 | 29 | function Shapes() { 30 | const [active, setActive] = useState(false); 31 | useEffect(() => { 32 | const interval = setInterval(() => setActive(a => !a), 500); 33 | return () => clearInterval(interval); 34 | }, []); 35 | 36 | return ( 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | {new Array(32 * 32).fill(0).map((_, i) => ( 45 | 46 | 47 | 48 | ))} 49 | 50 | 51 | ); 52 | } 53 | 54 | function App() { 55 | return ( 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | ); 67 | } 68 | 69 | ReactDOM.render(, document.getElementById('root')); 70 | ``` 71 | 72 | ## Docs 73 | 74 | ### The MapComponent component 75 | 76 | The most important component in react-ol-fiber is `` it instantiate an OpenLayer `Map` object and mounts it in a full-width and full-height div. As children you can provide OpenLayer elements that can be mounted inside the map, such as layers, controls and interactions. 77 | 78 | ```tsx 79 | function App() { 80 | return ( 81 | 87 | 88 | 89 | 90 | 91 | ); 92 | } 93 | ``` 94 | 95 | ### Using OpenLayers classes 96 | 97 | To create instances of OpenLayers classes in react-ol-fiber you can use JSX primitives. As component name, use the original class name with the first letter in lower case, followed by its category. 98 | 99 | To provide arguments to the class constructor use the `args` prop, or `arg` if the constructor has a single parameter. To attach the children to the parent you can use the `attach` and `attachAdd` props (even though they are inferred automatically whenever possible by the reconciler). 100 | 101 | Some examples: 102 | 103 | ```tsx 104 | function Component() { 105 | return ( 106 | <> 107 | {/* ol/Feature */} 108 | {/* ol/layers/Tile */} 109 | {/* ol/layers/Vector */} 110 | {/* ol/geom/Circle */} 111 | {/* ol/geom/Point */} 112 | {/* ol/geom/Point */} 113 | {/* ol/style/Style */} 114 | {/* ol/style/Stroke */} 115 | > 116 | ); 117 | } 118 | ``` 119 | 120 | ### Props 121 | 122 | The props are applied using the setters found in the target object. The reconciler is optimized to call only the setters of the modified values. 123 | 124 | ```tsx 125 | function Component() { 126 | // This will call setOpacity in the VectorLayer 127 | return ; 128 | } 129 | ``` 130 | 131 | ### Event handlers 132 | 133 | All the events described in the OpenLayers documentation are capitalized and prefixed with "on". 134 | 135 | ```tsx 136 | function Component() { 137 | // This will set the 'select' event 138 | return console.log(e)} />; 139 | 140 | // This will set the 'change' event 141 | return console.log(e)} />; 142 | 143 | // It also works on the map component! 144 | return console.log(e.coordinate)} />; 145 | } 146 | ``` 147 | 148 | ### Hooks 149 | 150 | Whenever you need to access the underlying OpenLayers map instance, you can use the `useOL()` hook. Remember that this can work only inside a component that is child of a MapComponent. :warning: 151 | 152 | ```tsx 153 | function Inner() { 154 | const { map } = useOL(); 155 | function centerOnFeatures(extent: number[]) { 156 | const view = map.getView(); 157 | view.fit(extent); 158 | } 159 | 160 | return ( 161 | 162 | centerOnFeatures(e.target.getExtent())}> 163 | 164 | 165 | 166 | 167 | 168 | ); 169 | } 170 | 171 | function Parent() { 172 | // WARNING: you can't use useOL() here 173 | return ( 174 | 175 | 176 | 177 | ); 178 | } 179 | ``` 180 | 181 | ### Spring Animation 182 | 183 | Provisional [react-spring](https://react-spring.io/) support is available! You can use the spring api to animate your maps, using the `a.` components. See [this example](https://github.com/giulioz/react-ol-fiber/blob/main/example/src/examples/Spring.tsx) to see how. 184 | 185 | ### Using primitives 186 | 187 | If you want to use your own already instanced objects, you can use the `olPrimitive` wrapper and set a custom attach: 188 | 189 | ```tsx 190 | function Component() { 191 | const features = myLoadFeatures(); 192 | return ( 193 | 194 | {features.map((feature, i) => ( 195 | 196 | ))} 197 | 198 | ); 199 | } 200 | ``` 201 | 202 | :warning: Using the `` instrinsic the props will not be checked. To have a generic primitive component, based on the `object` prop type, use the `` wrapper instead. 203 | 204 | ### Using functions 205 | 206 | Sometimes in OpenLayers it's convenient to use a function for some objects, such as style functions, to avoid creating too many objects. 207 | 208 | ```tsx 209 | function Component() { 210 | return ( 211 | 212 | new Style({ fill: new Fill({ color: feature.get('color') }) })} attach='style' /> 213 | 214 | {/* OR */} 215 | 216 | ( 218 | 219 | 220 | 221 | )} 222 | attach='style' 223 | /> 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | ); 232 | } 233 | ``` 234 | 235 | :warning: Please note that the second option, using JSX, does NOT use React to render the elements, it manually creates instances reading the JSX. Please use it with caution and DON'T use components in there. 236 | 237 | ### Extending the catalogue 238 | 239 | To extend the available components reachable by react-ol-fiber, you can use the `extend()` command. You can even implement your own props application logic using setters! 240 | 241 | ```tsx 242 | import BaseLayer from 'ol/layer/Base'; 243 | class MyLayer extends BaseLayer { 244 | constructor(args: { ctorArg: boolean }) { 245 | super({}); 246 | } 247 | 248 | setMyNumber(value: number) { 249 | console.log(value); 250 | } 251 | } 252 | 253 | import { extend, MapComponent, TypeOLCustomClass } from 'react-ol-fiber'; 254 | extend({ MyLayer: MyLayer as any }); 255 | declare global { 256 | namespace JSX { 257 | interface IntrinsicElements { 258 | myLayer: TypeOLCustomClass; 259 | } 260 | } 261 | } 262 | 263 | function Test() { 264 | return ( 265 | 266 | 267 | 268 | ); 269 | } 270 | ``` 271 | 272 | ## FAQ 273 | 274 | ### I'm not seeing my map and the entire page is blank 275 | 276 | You need to add make your parent DOM elements full-height: 277 | 278 | ```css 279 | html, 280 | body, 281 | #root { 282 | width: 100%; 283 | height: 100%; 284 | margin: 0; 285 | } 286 | ``` 287 | 288 | ## Credits 289 | 290 | This library was strongly inspired by react-three-fiber and the technical details given by this amazing article by Cody Bennet. 291 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { test: { presets: [['@babel/preset-env', { targets: { node: 'current' } }]] } }, 3 | }; 4 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | React-OL-Fiber Example 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "1.0.0", 4 | "private": true, 5 | "license": "MIT", 6 | "scripts": { 7 | "start": "vite", 8 | "build": "vite build" 9 | }, 10 | "dependencies": { 11 | "@emotion/react": "^11.7.1", 12 | "@emotion/styled": "^11.6.0", 13 | "@mui/icons-material": "^5.2.5", 14 | "@mui/material": "^5.2.7", 15 | "@react-spring/core": "^9.4.2", 16 | "@react-spring/web": "^9.4.2", 17 | "ol": "^6.9.0", 18 | "react-router-dom": "^5.3.0", 19 | "use-asset": "^1.0.4" 20 | }, 21 | "alias": { 22 | "react": "../node_modules/react", 23 | "react-dom": "../node_modules/react-dom/profiling", 24 | "react-ol-fiber": "../.", 25 | "scheduler/tracing": "../node_modules/scheduler/tracing-profiling" 26 | }, 27 | "devDependencies": { 28 | "@types/react": "^17.0.14", 29 | "@types/react-dom": "^17.0.9", 30 | "@types/react-router-dom": "^5.3.3", 31 | "typescript": "^4.3.5", 32 | "vite": "^2.7.10" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /example/src/components/SeeCodeButton.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Box, Button } from '@mui/material'; 3 | import LinkIcon from '@mui/icons-material/Code'; 4 | import DudeIcon from '@mui/icons-material/Laptop'; 5 | import BackIcon from '@mui/icons-material/ArrowBack'; 6 | 7 | export function SeeCodeButton({ url, home }: { url: string; home?: boolean }) { 8 | return ( 9 | <> 10 | {!home && ( 11 | 12 | } href='/'> 13 | Home 14 | 15 | 16 | )} 17 | 18 | 19 | } href='https://giuliozausa.dev/'> 20 | Giulio Zausa 21 | 22 | 23 | 24 | 25 | } href={url}> 26 | Code 27 | 28 | 29 | > 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /example/src/examples/Annotator.tsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/giulioz/react-ol-fiber/ba9d35c10bac124070fee3d404fbcddde64230c1/example/src/examples/Annotator.tsx -------------------------------------------------------------------------------- /example/src/examples/Intro.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | import { createTheme, CssBaseline, ThemeProvider, Button, Stack, Typography, Box } from '@mui/material'; 4 | 5 | import { MapComponent, useOL } from '../../../src'; 6 | import { DarkCanvasLayer } from '../ol-components/DarkCanvasLayer'; 7 | import { SeeCodeButton } from '../components/SeeCodeButton'; 8 | 9 | const theme = createTheme({ 10 | palette: { mode: 'dark', secondary: { main: '#fff' } }, 11 | }); 12 | 13 | function MapRotator() { 14 | const { map } = useOL(); 15 | useEffect(() => { 16 | function loop() { 17 | map.getView().adjustCenter([5000, 0]); 18 | requestAnimationFrame(loop); 19 | } 20 | requestAnimationFrame(loop); 21 | }, [map]); 22 | 23 | return null; 24 | } 25 | 26 | export function Intro() { 27 | return ( 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | React-OL-Fiber 42 | 43 | 44 | GitHub / Docs 45 | 46 | 47 | 48 | 49 | Examples 50 | 51 | 52 | Spring Animations 53 | 54 | 55 | Regions Explorer 56 | 57 | 58 | 59 | 60 | 61 | 62 | ); 63 | } 64 | -------------------------------------------------------------------------------- /example/src/examples/RegionsExplorer.tsx: -------------------------------------------------------------------------------- 1 | import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; 2 | import { Box, CssBaseline, ThemeProvider, createTheme, Typography, Button, Stack } from '@mui/material'; 3 | import { Feature, MapBrowserEvent } from 'ol'; 4 | import { Polygon } from 'ol/geom'; 5 | import VectorSource from 'ol/source/Vector'; 6 | import GeoJSON from 'ol/format/GeoJSON'; 7 | import { MapComponent, useOL } from '../../../src'; 8 | 9 | import { SeeCodeButton } from '../components/SeeCodeButton'; 10 | import { DarkCanvasLayer } from '../ol-components/DarkCanvasLayer'; 11 | import italyRegions from '../../data/italy-regions.json'; 12 | import '../style.css'; 13 | 14 | function RegionsOutlinesLayer({ 15 | hovered, 16 | onHover, 17 | clicked, 18 | onClick, 19 | }: { 20 | hovered: string | null; 21 | onHover: (name: string) => void; 22 | clicked: string | null; 23 | onClick: (name: string) => void; 24 | }) { 25 | const features = useMemo(() => new GeoJSON().readFeatures(italyRegions, { featureProjection: 'EPSG:3857' }), []); 26 | const sourceRef = useRef>(null); 27 | 28 | const { map } = useOL(); 29 | const centerOnFeatures = useCallback( 30 | (extent: number[], padding = 50) => { 31 | const view = map.getView(); 32 | view.fit(extent, { padding: [padding, padding, padding, padding] }); 33 | }, 34 | [map], 35 | ); 36 | 37 | useEffect(() => { 38 | const feature = features.find(f => f.get('reg_name') === clicked) as Feature | null; 39 | const geometry = feature?.getGeometry(); 40 | if (geometry) { 41 | centerOnFeatures(geometry.getExtent()); 42 | } else if (sourceRef.current) { 43 | centerOnFeatures(sourceRef.current.getExtent()); 44 | } 45 | }, [clicked, centerOnFeatures, features]); 46 | 47 | return ( 48 | <> 49 | 50 | 51 | {features.map((feature, i) => ( 52 | 53 | 54 | 55 | 65 | 66 | 67 | ))} 68 | 69 | 70 | 71 | ) => onHover(map.getFeaturesAtPixel(e.pixel)[0]?.get('reg_name')), 74 | handleDownEvent: (e: MapBrowserEvent) => onClick(map.getFeaturesAtPixel(e.pixel)[0]?.get('reg_name')), 75 | }} 76 | /> 77 | > 78 | ); 79 | } 80 | 81 | const theme = createTheme({ 82 | palette: { mode: 'dark', primary: { main: '#ab760c' }, secondary: { main: '#fff' } }, 83 | }); 84 | 85 | export function RegionsExplorer() { 86 | const [hovered, setHovered] = useState(null); 87 | const [clicked, setClicked] = useState(null); 88 | 89 | return ( 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | {clicked && ( 99 | 100 | setClicked(null)}> 101 | Back to global view 102 | 103 | 104 | )} 105 | 106 | 107 | 108 | {hovered} 109 | 110 | 111 | 112 | 113 | 114 | ); 115 | } 116 | -------------------------------------------------------------------------------- /example/src/examples/Spring.tsx: -------------------------------------------------------------------------------- 1 | import React, { useMemo, useState } from 'react'; 2 | import { CssBaseline, ThemeProvider, createTheme, Typography, Button, Stack } from '@mui/material'; 3 | import { Feature } from 'ol'; 4 | import { Point } from 'ol/geom'; 5 | import GeoJSON from 'ol/format/GeoJSON'; 6 | import { useSpring, config, useSpringRef, useTransition, useChain } from '@react-spring/core'; 7 | import { animated } from '@react-spring/web'; 8 | import { MapComponent, a } from '../../../src'; 9 | 10 | import { SeeCodeButton } from '../components/SeeCodeButton'; 11 | import { DarkCanvasLayer } from '../ol-components/DarkCanvasLayer'; 12 | import usaCities from '../../data/usa-cities.json'; 13 | import '../style.css'; 14 | 15 | const theme = createTheme({ 16 | palette: { mode: 'dark', primary: { main: '#fff' }, secondary: { main: '#fff' } }, 17 | }); 18 | 19 | export function Spring() { 20 | const cities = useMemo(() => new GeoJSON().readFeatures(usaCities, { featureProjection: 'EPSG:3857' }) as Feature[], []); 21 | 22 | const [currentCity, setCurrentCity] = useState(Math.floor(Math.random() * cities.length)); 23 | const city = cities[currentCity]; 24 | const viewSpringRef = useSpringRef(); 25 | const viewSpring = useSpring({ pos: city.getGeometry()?.getCoordinates() || [0, 0], zoom: 7, config: config.stiff, ref: viewSpringRef }); 26 | 27 | const bulletTransitionRef = useSpringRef(); 28 | const bulletTransition = useTransition(currentCity, { 29 | from: { opacity: 0, radius: 0 }, 30 | enter: { opacity: 0.8, radius: 20000 }, 31 | leave: { opacity: 0, radius: 0 }, 32 | exitBeforeEnter: true, 33 | config: { duration: 500 }, 34 | ref: bulletTransitionRef, 35 | }); 36 | 37 | useChain([bulletTransitionRef, viewSpringRef], [0, 0.7]); 38 | 39 | const [overlayDiv, setOverlayDiv] = useState(null); 40 | 41 | return ( 42 | 43 | 44 | 45 | 46 | 47 | 55 | 56 | {bulletTransition(({ opacity, radius }, i) => { 57 | const cityCoords = cities[i].getGeometry()?.getCoordinates() || [0, 0]; 58 | return ( 59 | <> 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | {overlayDiv && } 73 | > 74 | ); 75 | })} 76 | 77 | 78 | 79 | {bulletTransition(({ opacity }, i) => ( 80 | 81 | 82 | {cities[i].get('NAME')} 83 | 84 | 85 | POP. {cities[i].get('POPULATION')} 86 | 87 | 88 | ))} 89 | 90 | 91 | 92 | setCurrentCity(Math.floor(Math.random() * cities.length))} 97 | > 98 | Go to random city 99 | 100 | 101 | 102 | 103 | 104 | ); 105 | } 106 | -------------------------------------------------------------------------------- /example/src/examples/experiment.tsx: -------------------------------------------------------------------------------- 1 | // import { 2 | // AppBar, 3 | // CssBaseline, 4 | // IconButton, 5 | // Stack, 6 | // Toolbar, 7 | // Typography, 8 | // createTheme, 9 | // ThemeProvider, 10 | // Drawer, 11 | // List, 12 | // ListItem, 13 | // ListItemIcon, 14 | // ListItemText, 15 | // Box, 16 | // } from '@mui/material'; 17 | // import MenuIcon from '@mui/icons-material/Menu'; 18 | // import { Feature } from 'ol'; 19 | // import VectorSource from 'ol/source/Vector'; 20 | // import GeoJSON from 'ol/format/GeoJSON'; 21 | // import { getCenter } from 'ol/extent'; 22 | // import 'ol/ol.css'; 23 | 24 | // import { MapComponent, useOL } from '../../src'; 25 | // import { DarkCanvasLayer } from './ol-components/DarkCanvasLayer'; 26 | // import usaCities from '../data/usa-cities.json'; 27 | // import italyRegions from '../data/italy-regions.json'; 28 | // import './style.css'; 29 | 30 | // const theme = createTheme({ 31 | // palette: { 32 | // mode: 'dark', 33 | // primary: { main: '#0284C7' }, 34 | // background: { paper: '#1E293B' }, 35 | // }, 36 | // }); 37 | 38 | // function Inner() { 39 | // const [features, setFeatures] = useState[]>([]); 40 | // const [styled, setStyled] = useState(false); 41 | 42 | // useEffect(() => { 43 | // async function load() { 44 | // const geoJson = new GeoJSON().readFeatures(italyRegions, { featureProjection: 'EPSG:3857' }); 45 | // setFeatures(geoJson); 46 | // } 47 | // load(); 48 | 49 | // const interval = setInterval(() => setStyled(a => !a), 500); 50 | // return () => clearInterval(interval); 51 | // }, []); 52 | 53 | // const { map } = useOL(); 54 | // const [extent, setExtent] = useState([0, 0, 0, 0]); 55 | // useEffect(() => { 56 | // const view = map.getView(); 57 | // view.fit(extent, { padding: [100, 100, 100, 100] }); 58 | // }, [extent]); 59 | 60 | // return ( 61 | // <> 62 | // 63 | 64 | // 65 | 66 | // 67 | // 68 | // {/* 69 | // 70 | // 71 | // */} 72 | // 73 | // 74 | 75 | // setExtent(e?.target?.getExtent())}> 76 | // {features.map((feature, i) => ( 77 | // 78 | // ))} 79 | // 80 | // 81 | 82 | // 83 | // 84 | // > 85 | // ); 86 | // } 87 | 88 | // function App2() { 89 | // const [drawerOpen, setDrawerOpen] = useState(true); 90 | 91 | // const [mousePos, setMousePos] = useState([0, 0]); 92 | 93 | // return ( 94 | // 95 | // 96 | 97 | // 98 | // theme.zIndex.drawer + 1 }}> 99 | // 100 | // setDrawerOpen(a => !a)}> 101 | // 102 | // 103 | // 104 | // React-OL-Fiber 105 | // 106 | // 107 | // 108 | 109 | // 110 | // 123 | // 124 | // 125 | // Layers 126 | // 127 | // 128 | // {['USA Cities', 'Italian Regions'].map((text, index) => ( 129 | // 130 | // 131 | // 132 | // ))} 133 | // 134 | // 135 | 136 | // 137 | // setMousePos(e.coordinate)}> 138 | // 139 | // 140 | // 141 | // 142 | 143 | // 144 | // Coords: {mousePos.map(c => c.toFixed(3)).join(', ')} 145 | // 146 | // 147 | // 148 | // ); 149 | // } 150 | -------------------------------------------------------------------------------- /example/src/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { Suspense } from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { BrowserRouter, Route, Switch } from 'react-router-dom'; 4 | 5 | import { RegionsExplorer } from './examples/RegionsExplorer'; 6 | import { Spring } from './examples/Spring'; 7 | import { Intro } from './examples/Intro'; 8 | 9 | function App() { 10 | return ( 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | ); 27 | } 28 | 29 | ReactDOM.render(, document.getElementById('root')); 30 | -------------------------------------------------------------------------------- /example/src/ol-components/DarkCanvasLayer.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export function DarkCanvasLayer() { 4 | return ( 5 | 6 | 9 | 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /example/src/style.css: -------------------------------------------------------------------------------- 1 | html, 2 | body, 3 | #root { 4 | width: 100%; 5 | height: 100%; 6 | margin: 0; 7 | } 8 | -------------------------------------------------------------------------------- /example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "esModuleInterop": true, 7 | "jsx": "react", 8 | "pretty": true, 9 | "strict": true, 10 | "skipLibCheck": true, 11 | "declaration": true, 12 | "declarationDir": "dist", 13 | "removeComments": true, 14 | "emitDeclarationOnly": true, 15 | "resolveJsonModule": true, 16 | "noImplicitThis": false, 17 | "baseUrl": "./", 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /example/vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "routes": [{ "src": "/[^.]+", "dest": "/", "status": 200 }] 3 | } 4 | -------------------------------------------------------------------------------- /example/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@babel/code-frame@^7.0.0": 6 | version "7.16.7" 7 | resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" 8 | integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== 9 | dependencies: 10 | "@babel/highlight" "^7.16.7" 11 | 12 | "@babel/helper-module-imports@^7.12.13": 13 | version "7.16.7" 14 | resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437" 15 | integrity sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg== 16 | dependencies: 17 | "@babel/types" "^7.16.7" 18 | 19 | "@babel/helper-plugin-utils@^7.16.7": 20 | version "7.16.7" 21 | resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz#aa3a8ab4c3cceff8e65eb9e73d87dc4ff320b2f5" 22 | integrity sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA== 23 | 24 | "@babel/helper-validator-identifier@^7.16.7": 25 | version "7.16.7" 26 | resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" 27 | integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== 28 | 29 | "@babel/highlight@^7.16.7": 30 | version "7.16.10" 31 | resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.10.tgz#744f2eb81579d6eea753c227b0f570ad785aba88" 32 | integrity sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw== 33 | dependencies: 34 | "@babel/helper-validator-identifier" "^7.16.7" 35 | chalk "^2.0.0" 36 | js-tokens "^4.0.0" 37 | 38 | "@babel/plugin-syntax-jsx@^7.12.13": 39 | version "7.16.7" 40 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz#50b6571d13f764266a113d77c82b4a6508bbe665" 41 | integrity sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q== 42 | dependencies: 43 | "@babel/helper-plugin-utils" "^7.16.7" 44 | 45 | "@babel/runtime@^7.1.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.13.10", "@babel/runtime@^7.16.7", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.7": 46 | version "7.16.7" 47 | resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.7.tgz#03ff99f64106588c9c403c6ecb8c3bafbbdff1fa" 48 | integrity sha512-9E9FJowqAsytyOY6LG+1KuueckRL+aQW+mKvXRXnuFGyRAyepJPmEo9vgMfXUA6O9u3IeEdv9MAkppFcaQwogQ== 49 | dependencies: 50 | regenerator-runtime "^0.13.4" 51 | 52 | "@babel/types@^7.16.7": 53 | version "7.16.8" 54 | resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.16.8.tgz#0ba5da91dd71e0a4e7781a30f22770831062e3c1" 55 | integrity sha512-smN2DQc5s4M7fntyjGtyIPbRJv6wW4rU/94fmYJ7PKQuZkC0qGMHXJbg6sNGt12JmVr4k5YaptI/XtiLJBnmIg== 56 | dependencies: 57 | "@babel/helper-validator-identifier" "^7.16.7" 58 | to-fast-properties "^2.0.0" 59 | 60 | "@emotion/babel-plugin@^11.3.0": 61 | version "11.7.2" 62 | resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.7.2.tgz#fec75f38a6ab5b304b0601c74e2a5e77c95e5fa0" 63 | integrity sha512-6mGSCWi9UzXut/ZAN6lGFu33wGR3SJisNl3c0tvlmb8XChH1b2SUvxvnOh7hvLpqyRdHHU9AiazV3Cwbk5SXKQ== 64 | dependencies: 65 | "@babel/helper-module-imports" "^7.12.13" 66 | "@babel/plugin-syntax-jsx" "^7.12.13" 67 | "@babel/runtime" "^7.13.10" 68 | "@emotion/hash" "^0.8.0" 69 | "@emotion/memoize" "^0.7.5" 70 | "@emotion/serialize" "^1.0.2" 71 | babel-plugin-macros "^2.6.1" 72 | convert-source-map "^1.5.0" 73 | escape-string-regexp "^4.0.0" 74 | find-root "^1.1.0" 75 | source-map "^0.5.7" 76 | stylis "4.0.13" 77 | 78 | "@emotion/cache@^11.7.1": 79 | version "11.7.1" 80 | resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.7.1.tgz#08d080e396a42e0037848214e8aa7bf879065539" 81 | integrity sha512-r65Zy4Iljb8oyjtLeCuBH8Qjiy107dOYC6SJq7g7GV5UCQWMObY4SJDPGFjiiVpPrOJ2hmJOoBiYTC7hwx9E2A== 82 | dependencies: 83 | "@emotion/memoize" "^0.7.4" 84 | "@emotion/sheet" "^1.1.0" 85 | "@emotion/utils" "^1.0.0" 86 | "@emotion/weak-memoize" "^0.2.5" 87 | stylis "4.0.13" 88 | 89 | "@emotion/hash@^0.8.0": 90 | version "0.8.0" 91 | resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413" 92 | integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow== 93 | 94 | "@emotion/is-prop-valid@^1.1.1": 95 | version "1.1.1" 96 | resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.1.1.tgz#cbd843d409dfaad90f9404e7c0404c55eae8c134" 97 | integrity sha512-bW1Tos67CZkOURLc0OalnfxtSXQJMrAMV0jZTVGJUPSOd4qgjF3+tTD5CwJM13PHA8cltGW1WGbbvV9NpvUZPw== 98 | dependencies: 99 | "@emotion/memoize" "^0.7.4" 100 | 101 | "@emotion/memoize@^0.7.4", "@emotion/memoize@^0.7.5": 102 | version "0.7.5" 103 | resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.5.tgz#2c40f81449a4e554e9fc6396910ed4843ec2be50" 104 | integrity sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ== 105 | 106 | "@emotion/react@^11.7.1": 107 | version "11.7.1" 108 | resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.7.1.tgz#3f800ce9b20317c13e77b8489ac4a0b922b2fe07" 109 | integrity sha512-DV2Xe3yhkF1yT4uAUoJcYL1AmrnO5SVsdfvu+fBuS7IbByDeTVx9+wFmvx9Idzv7/78+9Mgx2Hcmr7Fex3tIyw== 110 | dependencies: 111 | "@babel/runtime" "^7.13.10" 112 | "@emotion/cache" "^11.7.1" 113 | "@emotion/serialize" "^1.0.2" 114 | "@emotion/sheet" "^1.1.0" 115 | "@emotion/utils" "^1.0.0" 116 | "@emotion/weak-memoize" "^0.2.5" 117 | hoist-non-react-statics "^3.3.1" 118 | 119 | "@emotion/serialize@^1.0.2": 120 | version "1.0.2" 121 | resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.0.2.tgz#77cb21a0571c9f68eb66087754a65fa97bfcd965" 122 | integrity sha512-95MgNJ9+/ajxU7QIAruiOAdYNjxZX7G2mhgrtDWswA21VviYIRP1R5QilZ/bDY42xiKsaktP4egJb3QdYQZi1A== 123 | dependencies: 124 | "@emotion/hash" "^0.8.0" 125 | "@emotion/memoize" "^0.7.4" 126 | "@emotion/unitless" "^0.7.5" 127 | "@emotion/utils" "^1.0.0" 128 | csstype "^3.0.2" 129 | 130 | "@emotion/sheet@^1.1.0": 131 | version "1.1.0" 132 | resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.1.0.tgz#56d99c41f0a1cda2726a05aa6a20afd4c63e58d2" 133 | integrity sha512-u0AX4aSo25sMAygCuQTzS+HsImZFuS8llY8O7b9MDRzbJM0kVJlAz6KNDqcG7pOuQZJmj/8X/rAW+66kMnMW+g== 134 | 135 | "@emotion/styled@^11.6.0": 136 | version "11.6.0" 137 | resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.6.0.tgz#9230d1a7bcb2ebf83c6a579f4c80e0664132d81d" 138 | integrity sha512-mxVtVyIOTmCAkFbwIp+nCjTXJNgcz4VWkOYQro87jE2QBTydnkiYusMrRGFtzuruiGK4dDaNORk4gH049iiQuw== 139 | dependencies: 140 | "@babel/runtime" "^7.13.10" 141 | "@emotion/babel-plugin" "^11.3.0" 142 | "@emotion/is-prop-valid" "^1.1.1" 143 | "@emotion/serialize" "^1.0.2" 144 | "@emotion/utils" "^1.0.0" 145 | 146 | "@emotion/unitless@^0.7.5": 147 | version "0.7.5" 148 | resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed" 149 | integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg== 150 | 151 | "@emotion/utils@^1.0.0": 152 | version "1.0.0" 153 | resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.0.0.tgz#abe06a83160b10570816c913990245813a2fd6af" 154 | integrity sha512-mQC2b3XLDs6QCW+pDQDiyO/EdGZYOygE8s5N5rrzjSI4M3IejPE/JPndCBwRT9z982aqQNi6beWs1UeayrQxxA== 155 | 156 | "@emotion/weak-memoize@^0.2.5": 157 | version "0.2.5" 158 | resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz#8eed982e2ee6f7f4e44c253e12962980791efd46" 159 | integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA== 160 | 161 | "@mapbox/jsonlint-lines-primitives@~2.0.2": 162 | version "2.0.2" 163 | resolved "https://registry.yarnpkg.com/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz#ce56e539f83552b58d10d672ea4d6fc9adc7b234" 164 | integrity sha1-zlblOfg1UrWNENZy6k1vya3HsjQ= 165 | 166 | "@mapbox/mapbox-gl-style-spec@^13.20.1": 167 | version "13.23.0" 168 | resolved "https://registry.yarnpkg.com/@mapbox/mapbox-gl-style-spec/-/mapbox-gl-style-spec-13.23.0.tgz#dca481f78e6affd173c9c76fd9fde013b3cde41b" 169 | integrity sha512-zI26XoK0UjGOvOEUUAoKlmFKHrSD8qIMCaoQBsFxNPzGIluryT32Z1m4aq7NtxEsrfE+qc2mPPXQg+iRllqbqA== 170 | dependencies: 171 | "@mapbox/jsonlint-lines-primitives" "~2.0.2" 172 | "@mapbox/point-geometry" "^0.1.0" 173 | "@mapbox/unitbezier" "^0.0.0" 174 | csscolorparser "~1.0.2" 175 | json-stringify-pretty-compact "^2.0.0" 176 | minimist "^1.2.5" 177 | rw "^1.3.3" 178 | sort-object "^0.3.2" 179 | 180 | "@mapbox/point-geometry@^0.1.0": 181 | version "0.1.0" 182 | resolved "https://registry.yarnpkg.com/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz#8a83f9335c7860effa2eeeca254332aa0aeed8f2" 183 | integrity sha1-ioP5M1x4YO/6Lu7KJUMyqgru2PI= 184 | 185 | "@mapbox/unitbezier@^0.0.0": 186 | version "0.0.0" 187 | resolved "https://registry.yarnpkg.com/@mapbox/unitbezier/-/unitbezier-0.0.0.tgz#15651bd553a67b8581fb398810c98ad86a34524e" 188 | integrity sha1-FWUb1VOme4WB+zmIEMmK2Go0Uk4= 189 | 190 | "@mui/base@5.0.0-alpha.65": 191 | version "5.0.0-alpha.65" 192 | resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-alpha.65.tgz#58fb0a4334a57904144b405d16e8deea24bc31b6" 193 | integrity sha512-6LCTWVoSnEoQuWdxA+Z1qqmlNK4aZj7LvCuLJzq3RVS2PskRuo1O3caVFxAzRu7xrY3zsyL/sUsJI+rdcjhuXw== 194 | dependencies: 195 | "@babel/runtime" "^7.16.7" 196 | "@emotion/is-prop-valid" "^1.1.1" 197 | "@mui/utils" "^5.3.0" 198 | "@popperjs/core" "^2.4.4" 199 | clsx "^1.1.1" 200 | prop-types "^15.7.2" 201 | react-is "^17.0.2" 202 | 203 | "@mui/icons-material@^5.2.5": 204 | version "5.3.0" 205 | resolved "https://registry.yarnpkg.com/@mui/icons-material/-/icons-material-5.3.0.tgz#3e3d5e9a66bcd06d2949c20b82a9688a458fc130" 206 | integrity sha512-1+dN2N8BgozmdMeHXQLrvSr1G/7Xc0NmAMLSvu8XA9RxhcTos/p66vrvpPASw2qvt14dkfeqyHwvbLRgAU9slw== 207 | dependencies: 208 | "@babel/runtime" "^7.16.7" 209 | 210 | "@mui/material@^5.2.7": 211 | version "5.3.0" 212 | resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.3.0.tgz#564395bc4dd96bd12e51884214330f22ee876408" 213 | integrity sha512-zLdlweBHrKpOwHFoMXA6FFgKQOONuX4sQhODcj9dk5uPeScuMDE26svcsrPam5Y1PKXZX78YEGHB5Jr5PHGpTA== 214 | dependencies: 215 | "@babel/runtime" "^7.16.7" 216 | "@mui/base" "5.0.0-alpha.65" 217 | "@mui/system" "^5.3.0" 218 | "@mui/types" "^7.1.0" 219 | "@mui/utils" "^5.3.0" 220 | "@types/react-transition-group" "^4.4.4" 221 | clsx "^1.1.1" 222 | csstype "^3.0.10" 223 | hoist-non-react-statics "^3.3.2" 224 | prop-types "^15.7.2" 225 | react-is "^17.0.2" 226 | react-transition-group "^4.4.2" 227 | 228 | "@mui/private-theming@^5.3.0": 229 | version "5.3.0" 230 | resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-5.3.0.tgz#1ec32766fc4467f221663a4945b6c972c7d2567b" 231 | integrity sha512-EBobUEyM9fMnteKrVPp8pTMUh81xXakyfdpkoh7Y19q9JpD2eh7QGAQVJVj0JBFlcUJD60NIE4K8rdokrRmLwg== 232 | dependencies: 233 | "@babel/runtime" "^7.16.7" 234 | "@mui/utils" "^5.3.0" 235 | prop-types "^15.7.2" 236 | 237 | "@mui/styled-engine@^5.3.0": 238 | version "5.3.0" 239 | resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-5.3.0.tgz#b260a06398fc7335a62fd65ebbb9fc3c4071027b" 240 | integrity sha512-I4YemFy9WnCLUdZ5T+6egpzc8e7Jq/uh9AJ3QInZHbyNu/9I2SWvNn7vHjWOT/D8Y8LMzIOhu5WwZbzjez7YRw== 241 | dependencies: 242 | "@babel/runtime" "^7.16.7" 243 | "@emotion/cache" "^11.7.1" 244 | prop-types "^15.7.2" 245 | 246 | "@mui/system@^5.3.0": 247 | version "5.3.0" 248 | resolved "https://registry.yarnpkg.com/@mui/system/-/system-5.3.0.tgz#cd2c5fd7631f2c90f0072c866015bb24e319b66e" 249 | integrity sha512-mblz3EObrhhIMPwSEe2Az7MbMaXOFgrvItPOzZwcY5O9qERB7Rr8KQgbU8VouWLUqyV2i8BaFpLrkKPA6eX2Aw== 250 | dependencies: 251 | "@babel/runtime" "^7.16.7" 252 | "@mui/private-theming" "^5.3.0" 253 | "@mui/styled-engine" "^5.3.0" 254 | "@mui/types" "^7.1.0" 255 | "@mui/utils" "^5.3.0" 256 | clsx "^1.1.1" 257 | csstype "^3.0.10" 258 | prop-types "^15.7.2" 259 | 260 | "@mui/types@^7.1.0": 261 | version "7.1.0" 262 | resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.1.0.tgz#5ed928c5a41cfbf9a4be82ea3bbdc47bcc9610d5" 263 | integrity sha512-Hh7ALdq/GjfIwLvqH3XftuY3bcKhupktTm+S6qRIDGOtPtRuq2L21VWzOK4p7kblirK0XgGVH5BLwa6u8z/6QQ== 264 | 265 | "@mui/utils@^5.3.0": 266 | version "5.3.0" 267 | resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-5.3.0.tgz#5f31915063d25c56f1d3ba9e289bf447472a868c" 268 | integrity sha512-O/E9IQKPMg0OrN7+gkn7Ga5o5WA2iXQGdyqNBFPNrYzxOvwzsEtM5K7MtTCGGYKFe8mhTRM0ZOjh5OM0dglw+Q== 269 | dependencies: 270 | "@babel/runtime" "^7.16.7" 271 | "@types/prop-types" "^15.7.4" 272 | "@types/react-is" "^16.7.1 || ^17.0.0" 273 | prop-types "^15.7.2" 274 | react-is "^17.0.2" 275 | 276 | "@petamoriken/float16@^3.4.7": 277 | version "3.6.1" 278 | resolved "https://registry.yarnpkg.com/@petamoriken/float16/-/float16-3.6.1.tgz#c49ae91e9fa26a1e3c15af7f95e2efa085100151" 279 | integrity sha512-SI0ovmGj/cUKYvxwbGl5TGDdFFCPLK3/zdOS0RCoYeYJMQZojsoiljIRwnBKxPFk/IsxI160Pk9n42O+XihQdw== 280 | 281 | "@popperjs/core@^2.4.4": 282 | version "2.11.2" 283 | resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.2.tgz#830beaec4b4091a9e9398ac50f865ddea52186b9" 284 | integrity sha512-92FRmppjjqz29VMJ2dn+xdyXZBrMlE42AV6Kq6BwjWV7CNUW1hs2FtxSNLQE+gJhaZ6AAmYuO9y8dshhcBl7vA== 285 | 286 | "@react-spring/animated@~9.4.0": 287 | version "9.4.2" 288 | resolved "https://registry.yarnpkg.com/@react-spring/animated/-/animated-9.4.2.tgz#1dc107233ce4a44b023abac829f3b2ea8327a128" 289 | integrity sha512-Dzum5Ho8e+LIAegAqRyoQFakD2IVH3ZQ2nsFXJorAFq3Xjv6IVPz/+TNxb/wSvnsMludfoF+ZIf319FSFmgD5w== 290 | dependencies: 291 | "@react-spring/shared" "~9.4.0" 292 | "@react-spring/types" "~9.4.0" 293 | 294 | "@react-spring/core@^9.4.2", "@react-spring/core@~9.4.0": 295 | version "9.4.2" 296 | resolved "https://registry.yarnpkg.com/@react-spring/core/-/core-9.4.2.tgz#c20249535b3acaead015d17e1c4ce839ec3d4c9f" 297 | integrity sha512-Ej/ULwdx8rQtMAWEpLgwbKcQEx6vPfjyG3cxLP05zAInpCoWkYpl+sXOp9tn3r99mTNQPTTt7BgQsSnmQA8+rQ== 298 | dependencies: 299 | "@react-spring/animated" "~9.4.0" 300 | "@react-spring/rafz" "~9.4.0" 301 | "@react-spring/shared" "~9.4.0" 302 | "@react-spring/types" "~9.4.0" 303 | 304 | "@react-spring/rafz@~9.4.0": 305 | version "9.4.2" 306 | resolved "https://registry.yarnpkg.com/@react-spring/rafz/-/rafz-9.4.2.tgz#40a663d407cd116d436f662c6849f8e5b56234b6" 307 | integrity sha512-rSm+G8E/XEEpnCGtT/xYN6o8VvEXlU8wN/hyKp4Q44XAZzGSMHLIFP7pY94/MmWsxCxjkw1AxUWhiFYxWrnI5Q== 308 | 309 | "@react-spring/shared@~9.4.0": 310 | version "9.4.2" 311 | resolved "https://registry.yarnpkg.com/@react-spring/shared/-/shared-9.4.2.tgz#45e103eee04f5e857ab2c575c2a84d3a883f0bfa" 312 | integrity sha512-mZtbQLpMm6Vy5+O1MSlY9KuAcMO8rdUQvtdnC7Or7y7xiZlnzj8oAILyO6Y2rD2ZC1PmgVS0gMev/8T+MykW+Q== 313 | dependencies: 314 | "@react-spring/rafz" "~9.4.0" 315 | "@react-spring/types" "~9.4.0" 316 | 317 | "@react-spring/types@~9.4.0": 318 | version "9.4.2" 319 | resolved "https://registry.yarnpkg.com/@react-spring/types/-/types-9.4.2.tgz#f0518f6d23a0b0f699a71976483323ac84bc4ace" 320 | integrity sha512-GGiIscTM+CEUNV52anj3g5FqAZKL2+eRKtvBOAlC99qGBbvJ3qTLImrUR/I3lXY7PRuLgzI6kh34quA1oUxWYQ== 321 | 322 | "@react-spring/web@^9.4.2": 323 | version "9.4.2" 324 | resolved "https://registry.yarnpkg.com/@react-spring/web/-/web-9.4.2.tgz#ea8bb224c236d10097da463ea9e950f0939b7a5f" 325 | integrity sha512-sWfA9NkVuvVOpjSlMkD2zcF6X3i8NSHTeH/uHCGKsN3mYqgkhvAF+E8GASO/H4KKGNhbRvgCZiwJXOtOGyUg6A== 326 | dependencies: 327 | "@react-spring/animated" "~9.4.0" 328 | "@react-spring/core" "~9.4.0" 329 | "@react-spring/shared" "~9.4.0" 330 | "@react-spring/types" "~9.4.0" 331 | 332 | "@types/history@^4.7.11": 333 | version "4.7.11" 334 | resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.11.tgz#56588b17ae8f50c53983a524fc3cc47437969d64" 335 | integrity sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA== 336 | 337 | "@types/parse-json@^4.0.0": 338 | version "4.0.0" 339 | resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" 340 | integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== 341 | 342 | "@types/prop-types@*", "@types/prop-types@^15.7.4": 343 | version "15.7.4" 344 | resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.4.tgz#fcf7205c25dff795ee79af1e30da2c9790808f11" 345 | integrity sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ== 346 | 347 | "@types/react-dom@^17.0.9": 348 | version "17.0.11" 349 | resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.11.tgz#e1eadc3c5e86bdb5f7684e00274ae228e7bcc466" 350 | integrity sha512-f96K3k+24RaLGVu/Y2Ng3e1EbZ8/cVJvypZWd7cy0ofCBaf2lcM46xNhycMZ2xGwbBjRql7hOlZ+e2WlJ5MH3Q== 351 | dependencies: 352 | "@types/react" "*" 353 | 354 | "@types/react-is@^16.7.1 || ^17.0.0": 355 | version "17.0.3" 356 | resolved "https://registry.yarnpkg.com/@types/react-is/-/react-is-17.0.3.tgz#2d855ba575f2fc8d17ef9861f084acc4b90a137a" 357 | integrity sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw== 358 | dependencies: 359 | "@types/react" "*" 360 | 361 | "@types/react-router-dom@^5.3.3": 362 | version "5.3.3" 363 | resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.3.3.tgz#e9d6b4a66fcdbd651a5f106c2656a30088cc1e83" 364 | integrity sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw== 365 | dependencies: 366 | "@types/history" "^4.7.11" 367 | "@types/react" "*" 368 | "@types/react-router" "*" 369 | 370 | "@types/react-router@*": 371 | version "5.1.18" 372 | resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-5.1.18.tgz#c8851884b60bc23733500d86c1266e1cfbbd9ef3" 373 | integrity sha512-YYknwy0D0iOwKQgz9v8nOzt2J6l4gouBmDnWqUUznltOTaon+r8US8ky8HvN0tXvc38U9m6z/t2RsVsnd1zM0g== 374 | dependencies: 375 | "@types/history" "^4.7.11" 376 | "@types/react" "*" 377 | 378 | "@types/react-transition-group@^4.4.4": 379 | version "4.4.4" 380 | resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.4.tgz#acd4cceaa2be6b757db61ed7b432e103242d163e" 381 | integrity sha512-7gAPz7anVK5xzbeQW9wFBDg7G++aPLAFY0QaSMOou9rJZpbuI58WAuJrgu+qR92l61grlnCUe7AFX8KGahAgug== 382 | dependencies: 383 | "@types/react" "*" 384 | 385 | "@types/react@*", "@types/react@^17.0.14": 386 | version "17.0.38" 387 | resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.38.tgz#f24249fefd89357d5fa71f739a686b8d7c7202bd" 388 | integrity sha512-SI92X1IA+FMnP3qM5m4QReluXzhcmovhZnLNm3pyeQlooi02qI7sLiepEYqT678uNiyc25XfCqxREFpy3W7YhQ== 389 | dependencies: 390 | "@types/prop-types" "*" 391 | "@types/scheduler" "*" 392 | csstype "^3.0.2" 393 | 394 | "@types/scheduler@*": 395 | version "0.16.2" 396 | resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" 397 | integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== 398 | 399 | ansi-styles@^3.2.1: 400 | version "3.2.1" 401 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" 402 | integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== 403 | dependencies: 404 | color-convert "^1.9.0" 405 | 406 | babel-plugin-macros@^2.6.1: 407 | version "2.8.0" 408 | resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz#0f958a7cc6556b1e65344465d99111a1e5e10138" 409 | integrity sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg== 410 | dependencies: 411 | "@babel/runtime" "^7.7.2" 412 | cosmiconfig "^6.0.0" 413 | resolve "^1.12.0" 414 | 415 | callsites@^3.0.0, callsites@^3.1.0: 416 | version "3.1.0" 417 | resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" 418 | integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== 419 | 420 | chalk@^2.0.0: 421 | version "2.4.2" 422 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" 423 | integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== 424 | dependencies: 425 | ansi-styles "^3.2.1" 426 | escape-string-regexp "^1.0.5" 427 | supports-color "^5.3.0" 428 | 429 | clsx@^1.1.1: 430 | version "1.1.1" 431 | resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188" 432 | integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA== 433 | 434 | color-convert@^1.9.0: 435 | version "1.9.3" 436 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" 437 | integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== 438 | dependencies: 439 | color-name "1.1.3" 440 | 441 | color-name@1.1.3: 442 | version "1.1.3" 443 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" 444 | integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= 445 | 446 | convert-source-map@^1.5.0: 447 | version "1.8.0" 448 | resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" 449 | integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== 450 | dependencies: 451 | safe-buffer "~5.1.1" 452 | 453 | cosmiconfig@^6.0.0: 454 | version "6.0.0" 455 | resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-6.0.0.tgz#da4fee853c52f6b1e6935f41c1a2fc50bd4a9982" 456 | integrity sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg== 457 | dependencies: 458 | "@types/parse-json" "^4.0.0" 459 | import-fresh "^3.1.0" 460 | parse-json "^5.0.0" 461 | path-type "^4.0.0" 462 | yaml "^1.7.2" 463 | 464 | csscolorparser@~1.0.2: 465 | version "1.0.3" 466 | resolved "https://registry.yarnpkg.com/csscolorparser/-/csscolorparser-1.0.3.tgz#b34f391eea4da8f3e98231e2ccd8df9c041f171b" 467 | integrity sha1-s085HupNqPPpgjHizNjfnAQfFxs= 468 | 469 | csstype@^3.0.10, csstype@^3.0.2: 470 | version "3.0.10" 471 | resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.10.tgz#2ad3a7bed70f35b965707c092e5f30b327c290e5" 472 | integrity sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA== 473 | 474 | debug@^4.2.0: 475 | version "4.3.3" 476 | resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" 477 | integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== 478 | dependencies: 479 | ms "2.1.2" 480 | 481 | dom-helpers@^5.0.1: 482 | version "5.2.1" 483 | resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902" 484 | integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA== 485 | dependencies: 486 | "@babel/runtime" "^7.8.7" 487 | csstype "^3.0.2" 488 | 489 | error-ex@^1.3.1: 490 | version "1.3.2" 491 | resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" 492 | integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== 493 | dependencies: 494 | is-arrayish "^0.2.1" 495 | 496 | esbuild-android-arm64@0.13.15: 497 | version "0.13.15" 498 | resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.13.15.tgz#3fc3ff0bab76fe35dd237476b5d2b32bb20a3d44" 499 | integrity sha512-m602nft/XXeO8YQPUDVoHfjyRVPdPgjyyXOxZ44MK/agewFFkPa8tUo6lAzSWh5Ui5PB4KR9UIFTSBKh/RrCmg== 500 | 501 | esbuild-darwin-64@0.13.15: 502 | version "0.13.15" 503 | resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.13.15.tgz#8e9169c16baf444eacec60d09b24d11b255a8e72" 504 | integrity sha512-ihOQRGs2yyp7t5bArCwnvn2Atr6X4axqPpEdCFPVp7iUj4cVSdisgvEKdNR7yH3JDjW6aQDw40iQFoTqejqxvQ== 505 | 506 | esbuild-darwin-arm64@0.13.15: 507 | version "0.13.15" 508 | resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.13.15.tgz#1b07f893b632114f805e188ddfca41b2b778229a" 509 | integrity sha512-i1FZssTVxUqNlJ6cBTj5YQj4imWy3m49RZRnHhLpefFIh0To05ow9DTrXROTE1urGTQCloFUXTX8QfGJy1P8dQ== 510 | 511 | esbuild-freebsd-64@0.13.15: 512 | version "0.13.15" 513 | resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.13.15.tgz#0b8b7eca1690c8ec94c75680c38c07269c1f4a85" 514 | integrity sha512-G3dLBXUI6lC6Z09/x+WtXBXbOYQZ0E8TDBqvn7aMaOCzryJs8LyVXKY4CPnHFXZAbSwkCbqiPuSQ1+HhrNk7EA== 515 | 516 | esbuild-freebsd-arm64@0.13.15: 517 | version "0.13.15" 518 | resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.13.15.tgz#2e1a6c696bfdcd20a99578b76350b41db1934e52" 519 | integrity sha512-KJx0fzEDf1uhNOZQStV4ujg30WlnwqUASaGSFPhznLM/bbheu9HhqZ6mJJZM32lkyfGJikw0jg7v3S0oAvtvQQ== 520 | 521 | esbuild-linux-32@0.13.15: 522 | version "0.13.15" 523 | resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.13.15.tgz#6fd39f36fc66dd45b6b5f515728c7bbebc342a69" 524 | integrity sha512-ZvTBPk0YWCLMCXiFmD5EUtB30zIPvC5Itxz0mdTu/xZBbbHJftQgLWY49wEPSn2T/TxahYCRDWun5smRa0Tu+g== 525 | 526 | esbuild-linux-64@0.13.15: 527 | version "0.13.15" 528 | resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.13.15.tgz#9cb8e4bcd7574e67946e4ee5f1f1e12386bb6dd3" 529 | integrity sha512-eCKzkNSLywNeQTRBxJRQ0jxRCl2YWdMB3+PkWFo2BBQYC5mISLIVIjThNtn6HUNqua1pnvgP5xX0nHbZbPj5oA== 530 | 531 | esbuild-linux-arm64@0.13.15: 532 | version "0.13.15" 533 | resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.13.15.tgz#3891aa3704ec579a1b92d2a586122e5b6a2bfba1" 534 | integrity sha512-bYpuUlN6qYU9slzr/ltyLTR9YTBS7qUDymO8SV7kjeNext61OdmqFAzuVZom+OLW1HPHseBfJ/JfdSlx8oTUoA== 535 | 536 | esbuild-linux-arm@0.13.15: 537 | version "0.13.15" 538 | resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.13.15.tgz#8a00e99e6a0c6c9a6b7f334841364d8a2b4aecfe" 539 | integrity sha512-wUHttDi/ol0tD8ZgUMDH8Ef7IbDX+/UsWJOXaAyTdkT7Yy9ZBqPg8bgB/Dn3CZ9SBpNieozrPRHm0BGww7W/jA== 540 | 541 | esbuild-linux-mips64le@0.13.15: 542 | version "0.13.15" 543 | resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.13.15.tgz#36b07cc47c3d21e48db3bb1f4d9ef8f46aead4f7" 544 | integrity sha512-KlVjIG828uFPyJkO/8gKwy9RbXhCEUeFsCGOJBepUlpa7G8/SeZgncUEz/tOOUJTcWMTmFMtdd3GElGyAtbSWg== 545 | 546 | esbuild-linux-ppc64le@0.13.15: 547 | version "0.13.15" 548 | resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.13.15.tgz#f7e6bba40b9a11eb9dcae5b01550ea04670edad2" 549 | integrity sha512-h6gYF+OsaqEuBjeesTBtUPw0bmiDu7eAeuc2OEH9S6mV9/jPhPdhOWzdeshb0BskRZxPhxPOjqZ+/OqLcxQwEQ== 550 | 551 | esbuild-netbsd-64@0.13.15: 552 | version "0.13.15" 553 | resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.13.15.tgz#a2fedc549c2b629d580a732d840712b08d440038" 554 | integrity sha512-3+yE9emwoevLMyvu+iR3rsa+Xwhie7ZEHMGDQ6dkqP/ndFzRHkobHUKTe+NCApSqG5ce2z4rFu+NX/UHnxlh3w== 555 | 556 | esbuild-openbsd-64@0.13.15: 557 | version "0.13.15" 558 | resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.13.15.tgz#b22c0e5806d3a1fbf0325872037f885306b05cd7" 559 | integrity sha512-wTfvtwYJYAFL1fSs8yHIdf5GEE4NkbtbXtjLWjM3Cw8mmQKqsg8kTiqJ9NJQe5NX/5Qlo7Xd9r1yKMMkHllp5g== 560 | 561 | esbuild-sunos-64@0.13.15: 562 | version "0.13.15" 563 | resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.13.15.tgz#d0b6454a88375ee8d3964daeff55c85c91c7cef4" 564 | integrity sha512-lbivT9Bx3t1iWWrSnGyBP9ODriEvWDRiweAs69vI+miJoeKwHWOComSRukttbuzjZ8r1q0mQJ8Z7yUsDJ3hKdw== 565 | 566 | esbuild-windows-32@0.13.15: 567 | version "0.13.15" 568 | resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.13.15.tgz#c96d0b9bbb52f3303322582ef8e4847c5ad375a7" 569 | integrity sha512-fDMEf2g3SsJ599MBr50cY5ve5lP1wyVwTe6aLJsM01KtxyKkB4UT+fc5MXQFn3RLrAIAZOG+tHC+yXObpSn7Nw== 570 | 571 | esbuild-windows-64@0.13.15: 572 | version "0.13.15" 573 | resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.13.15.tgz#1f79cb9b1e1bb02fb25cd414cb90d4ea2892c294" 574 | integrity sha512-9aMsPRGDWCd3bGjUIKG/ZOJPKsiztlxl/Q3C1XDswO6eNX/Jtwu4M+jb6YDH9hRSUflQWX0XKAfWzgy5Wk54JQ== 575 | 576 | esbuild-windows-arm64@0.13.15: 577 | version "0.13.15" 578 | resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.13.15.tgz#482173070810df22a752c686509c370c3be3b3c3" 579 | integrity sha512-zzvyCVVpbwQQATaf3IG8mu1IwGEiDxKkYUdA4FpoCHi1KtPa13jeScYDjlW0Qh+ebWzpKfR2ZwvqAQkSWNcKjA== 580 | 581 | esbuild@^0.13.12: 582 | version "0.13.15" 583 | resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.13.15.tgz#db56a88166ee373f87dbb2d8798ff449e0450cdf" 584 | integrity sha512-raCxt02HBKv8RJxE8vkTSCXGIyKHdEdGfUmiYb8wnabnaEmHzyW7DCHb5tEN0xU8ryqg5xw54mcwnYkC4x3AIw== 585 | optionalDependencies: 586 | esbuild-android-arm64 "0.13.15" 587 | esbuild-darwin-64 "0.13.15" 588 | esbuild-darwin-arm64 "0.13.15" 589 | esbuild-freebsd-64 "0.13.15" 590 | esbuild-freebsd-arm64 "0.13.15" 591 | esbuild-linux-32 "0.13.15" 592 | esbuild-linux-64 "0.13.15" 593 | esbuild-linux-arm "0.13.15" 594 | esbuild-linux-arm64 "0.13.15" 595 | esbuild-linux-mips64le "0.13.15" 596 | esbuild-linux-ppc64le "0.13.15" 597 | esbuild-netbsd-64 "0.13.15" 598 | esbuild-openbsd-64 "0.13.15" 599 | esbuild-sunos-64 "0.13.15" 600 | esbuild-windows-32 "0.13.15" 601 | esbuild-windows-64 "0.13.15" 602 | esbuild-windows-arm64 "0.13.15" 603 | 604 | escape-string-regexp@^1.0.5: 605 | version "1.0.5" 606 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 607 | integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= 608 | 609 | escape-string-regexp@^4.0.0: 610 | version "4.0.0" 611 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" 612 | integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== 613 | 614 | esm@^3.2.25: 615 | version "3.2.25" 616 | resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10" 617 | integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA== 618 | 619 | fast-deep-equal@^3.1.3: 620 | version "3.1.3" 621 | resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" 622 | integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== 623 | 624 | find-root@^1.1.0: 625 | version "1.1.0" 626 | resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4" 627 | integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng== 628 | 629 | fsevents@~2.3.2: 630 | version "2.3.2" 631 | resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" 632 | integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== 633 | 634 | function-bind@^1.1.1: 635 | version "1.1.1" 636 | resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" 637 | integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== 638 | 639 | geotiff@^1.0.8: 640 | version "1.0.9" 641 | resolved "https://registry.yarnpkg.com/geotiff/-/geotiff-1.0.9.tgz#a2037c1f672c0a11bfbac8b46bbc56f901e32198" 642 | integrity sha512-PY+q1OP8RtQZkx1630pVfC3hEkxFnGW9LwIF/glSzcalyShkrH+W8uM/M4RVY12j4QkDQvRXVKOpU65hq6t0iQ== 643 | dependencies: 644 | "@petamoriken/float16" "^3.4.7" 645 | lerc "^3.0.0" 646 | lru-cache "^6.0.0" 647 | pako "^2.0.4" 648 | parse-headers "^2.0.2" 649 | threads "^1.7.0" 650 | xml-utils "^1.0.2" 651 | 652 | has-flag@^3.0.0: 653 | version "3.0.0" 654 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" 655 | integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= 656 | 657 | has@^1.0.3: 658 | version "1.0.3" 659 | resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" 660 | integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== 661 | dependencies: 662 | function-bind "^1.1.1" 663 | 664 | history@^4.9.0: 665 | version "4.10.1" 666 | resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3" 667 | integrity sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew== 668 | dependencies: 669 | "@babel/runtime" "^7.1.2" 670 | loose-envify "^1.2.0" 671 | resolve-pathname "^3.0.0" 672 | tiny-invariant "^1.0.2" 673 | tiny-warning "^1.0.0" 674 | value-equal "^1.0.1" 675 | 676 | hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2: 677 | version "3.3.2" 678 | resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" 679 | integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== 680 | dependencies: 681 | react-is "^16.7.0" 682 | 683 | ieee754@^1.1.12: 684 | version "1.2.1" 685 | resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" 686 | integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== 687 | 688 | import-fresh@^3.1.0: 689 | version "3.3.0" 690 | resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" 691 | integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== 692 | dependencies: 693 | parent-module "^1.0.0" 694 | resolve-from "^4.0.0" 695 | 696 | is-arrayish@^0.2.1: 697 | version "0.2.1" 698 | resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" 699 | integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= 700 | 701 | is-core-module@^2.8.1: 702 | version "2.8.1" 703 | resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" 704 | integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== 705 | dependencies: 706 | has "^1.0.3" 707 | 708 | is-observable@^2.1.0: 709 | version "2.1.0" 710 | resolved "https://registry.yarnpkg.com/is-observable/-/is-observable-2.1.0.tgz#5c8d733a0b201c80dff7bb7c0df58c6a255c7c69" 711 | integrity sha512-DailKdLb0WU+xX8K5w7VsJhapwHLZ9jjmazqCJq4X12CTgqq73TKnbRcnSLuXYPOoLQgV5IrD7ePiX/h1vnkBw== 712 | 713 | isarray@0.0.1: 714 | version "0.0.1" 715 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" 716 | integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= 717 | 718 | "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: 719 | version "4.0.0" 720 | resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" 721 | integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== 722 | 723 | json-parse-even-better-errors@^2.3.0: 724 | version "2.3.1" 725 | resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" 726 | integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== 727 | 728 | json-stringify-pretty-compact@^2.0.0: 729 | version "2.0.0" 730 | resolved "https://registry.yarnpkg.com/json-stringify-pretty-compact/-/json-stringify-pretty-compact-2.0.0.tgz#e77c419f52ff00c45a31f07f4c820c2433143885" 731 | integrity sha512-WRitRfs6BGq4q8gTgOy4ek7iPFXjbra0H3PmDLKm2xnZ+Gh1HUhiKGgCZkSPNULlP7mvfu6FV/mOLhCarspADQ== 732 | 733 | lerc@^3.0.0: 734 | version "3.0.0" 735 | resolved "https://registry.yarnpkg.com/lerc/-/lerc-3.0.0.tgz#36f36fbd4ba46f0abf4833799fff2e7d6865f5cb" 736 | integrity sha512-Rm4J/WaHhRa93nCN2mwWDZFoRVF18G1f47C+kvQWyHGEZxFpTUi73p7lMVSAndyxGt6lJ2/CFbOcf9ra5p8aww== 737 | 738 | lines-and-columns@^1.1.6: 739 | version "1.2.4" 740 | resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" 741 | integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== 742 | 743 | loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0: 744 | version "1.4.0" 745 | resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" 746 | integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== 747 | dependencies: 748 | js-tokens "^3.0.0 || ^4.0.0" 749 | 750 | lru-cache@^6.0.0: 751 | version "6.0.0" 752 | resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" 753 | integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== 754 | dependencies: 755 | yallist "^4.0.0" 756 | 757 | mapbox-to-css-font@^2.4.1: 758 | version "2.4.1" 759 | resolved "https://registry.yarnpkg.com/mapbox-to-css-font/-/mapbox-to-css-font-2.4.1.tgz#41bf38faed36b7dab069828aa3654e4bd91a1eda" 760 | integrity sha512-QQ/iKiM43DM9+aujTL45Iz5o7gDeSFmy4LPl3HZmNcwCE++NxGazf+yFpY+wCb+YS23sDa1ghpo3zrNFOcHlow== 761 | 762 | mini-create-react-context@^0.4.0: 763 | version "0.4.1" 764 | resolved "https://registry.yarnpkg.com/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz#072171561bfdc922da08a60c2197a497cc2d1d5e" 765 | integrity sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ== 766 | dependencies: 767 | "@babel/runtime" "^7.12.1" 768 | tiny-warning "^1.0.3" 769 | 770 | minimist@^1.2.5: 771 | version "1.2.5" 772 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" 773 | integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== 774 | 775 | ms@2.1.2: 776 | version "2.1.2" 777 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" 778 | integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== 779 | 780 | nanoid@^3.1.30: 781 | version "3.2.0" 782 | resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.2.0.tgz#62667522da6673971cca916a6d3eff3f415ff80c" 783 | integrity sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA== 784 | 785 | object-assign@^4.1.1: 786 | version "4.1.1" 787 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" 788 | integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= 789 | 790 | observable-fns@^0.6.1: 791 | version "0.6.1" 792 | resolved "https://registry.yarnpkg.com/observable-fns/-/observable-fns-0.6.1.tgz#636eae4fdd1132e88c0faf38d33658cc79d87e37" 793 | integrity sha512-9gRK4+sRWzeN6AOewNBTLXir7Zl/i3GB6Yl26gK4flxz8BXVpD3kt8amREmWNb0mxYOGDotvE5a4N+PtGGKdkg== 794 | 795 | ol-mapbox-style@^6.8.2: 796 | version "6.8.3" 797 | resolved "https://registry.yarnpkg.com/ol-mapbox-style/-/ol-mapbox-style-6.8.3.tgz#e93b1a1a204060b63bbfc9c09ddf2c9465ec99ee" 798 | integrity sha512-aX8aC3QHTYk9a37l6dKcC22OUkFWz01eHShbXYdXABwT9ih1MLcjR32OYCWCKasKZMEXYKyeTh9vLhOtdfx/vA== 799 | dependencies: 800 | "@mapbox/mapbox-gl-style-spec" "^13.20.1" 801 | mapbox-to-css-font "^2.4.1" 802 | webfont-matcher "^1.1.0" 803 | 804 | ol@^6.9.0: 805 | version "6.12.0" 806 | resolved "https://registry.yarnpkg.com/ol/-/ol-6.12.0.tgz#0de51abad0aaeb0eca41cba3c6d26ee485a3e92b" 807 | integrity sha512-HR87aV//64aiGWbgzfsyRF5zFG+1nM1keRE4SugY0vmYyG/jjoij8qh3uaFHkFNQThdAy99sgLiQwTFk6AvGgw== 808 | dependencies: 809 | geotiff "^1.0.8" 810 | ol-mapbox-style "^6.8.2" 811 | pbf "3.2.1" 812 | rbush "^3.0.1" 813 | 814 | pako@^2.0.4: 815 | version "2.0.4" 816 | resolved "https://registry.yarnpkg.com/pako/-/pako-2.0.4.tgz#6cebc4bbb0b6c73b0d5b8d7e8476e2b2fbea576d" 817 | integrity sha512-v8tweI900AUkZN6heMU/4Uy4cXRc2AYNRggVmTR+dEncawDJgCdLMximOVA2p4qO57WMynangsfGRb5WD6L1Bg== 818 | 819 | parent-module@^1.0.0: 820 | version "1.0.1" 821 | resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" 822 | integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== 823 | dependencies: 824 | callsites "^3.0.0" 825 | 826 | parse-headers@^2.0.2: 827 | version "2.0.4" 828 | resolved "https://registry.yarnpkg.com/parse-headers/-/parse-headers-2.0.4.tgz#9eaf2d02bed2d1eff494331ce3df36d7924760bf" 829 | integrity sha512-psZ9iZoCNFLrgRjZ1d8mn0h9WRqJwFxM9q3x7iUjN/YT2OksthDJ5TiPCu2F38kS4zutqfW+YdVVkBZZx3/1aw== 830 | 831 | parse-json@^5.0.0: 832 | version "5.2.0" 833 | resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" 834 | integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== 835 | dependencies: 836 | "@babel/code-frame" "^7.0.0" 837 | error-ex "^1.3.1" 838 | json-parse-even-better-errors "^2.3.0" 839 | lines-and-columns "^1.1.6" 840 | 841 | path-parse@^1.0.7: 842 | version "1.0.7" 843 | resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" 844 | integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== 845 | 846 | path-to-regexp@^1.7.0: 847 | version "1.8.0" 848 | resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" 849 | integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA== 850 | dependencies: 851 | isarray "0.0.1" 852 | 853 | path-type@^4.0.0: 854 | version "4.0.0" 855 | resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" 856 | integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== 857 | 858 | pbf@3.2.1: 859 | version "3.2.1" 860 | resolved "https://registry.yarnpkg.com/pbf/-/pbf-3.2.1.tgz#b4c1b9e72af966cd82c6531691115cc0409ffe2a" 861 | integrity sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ== 862 | dependencies: 863 | ieee754 "^1.1.12" 864 | resolve-protobuf-schema "^2.1.0" 865 | 866 | picocolors@^1.0.0: 867 | version "1.0.0" 868 | resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" 869 | integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== 870 | 871 | postcss@^8.4.5: 872 | version "8.4.5" 873 | resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.5.tgz#bae665764dfd4c6fcc24dc0fdf7e7aa00cc77f95" 874 | integrity sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg== 875 | dependencies: 876 | nanoid "^3.1.30" 877 | picocolors "^1.0.0" 878 | source-map-js "^1.0.1" 879 | 880 | prop-types@^15.6.2, prop-types@^15.7.2: 881 | version "15.8.1" 882 | resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" 883 | integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== 884 | dependencies: 885 | loose-envify "^1.4.0" 886 | object-assign "^4.1.1" 887 | react-is "^16.13.1" 888 | 889 | protocol-buffers-schema@^3.3.1: 890 | version "3.6.0" 891 | resolved "https://registry.yarnpkg.com/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz#77bc75a48b2ff142c1ad5b5b90c94cd0fa2efd03" 892 | integrity sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw== 893 | 894 | quickselect@^2.0.0: 895 | version "2.0.0" 896 | resolved "https://registry.yarnpkg.com/quickselect/-/quickselect-2.0.0.tgz#f19680a486a5eefb581303e023e98faaf25dd018" 897 | integrity sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw== 898 | 899 | rbush@^3.0.1: 900 | version "3.0.1" 901 | resolved "https://registry.yarnpkg.com/rbush/-/rbush-3.0.1.tgz#5fafa8a79b3b9afdfe5008403a720cc1de882ecf" 902 | integrity sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w== 903 | dependencies: 904 | quickselect "^2.0.0" 905 | 906 | react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0: 907 | version "16.13.1" 908 | resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" 909 | integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== 910 | 911 | react-is@^17.0.2: 912 | version "17.0.2" 913 | resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" 914 | integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== 915 | 916 | react-router-dom@^5.3.0: 917 | version "5.3.0" 918 | resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.3.0.tgz#da1bfb535a0e89a712a93b97dd76f47ad1f32363" 919 | integrity sha512-ObVBLjUZsphUUMVycibxgMdh5jJ1e3o+KpAZBVeHcNQZ4W+uUGGWsokurzlF4YOldQYRQL4y6yFRWM4m3svmuQ== 920 | dependencies: 921 | "@babel/runtime" "^7.12.13" 922 | history "^4.9.0" 923 | loose-envify "^1.3.1" 924 | prop-types "^15.6.2" 925 | react-router "5.2.1" 926 | tiny-invariant "^1.0.2" 927 | tiny-warning "^1.0.0" 928 | 929 | react-router@5.2.1: 930 | version "5.2.1" 931 | resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.2.1.tgz#4d2e4e9d5ae9425091845b8dbc6d9d276239774d" 932 | integrity sha512-lIboRiOtDLFdg1VTemMwud9vRVuOCZmUIT/7lUoZiSpPODiiH1UQlfXy+vPLC/7IWdFYnhRwAyNqA/+I7wnvKQ== 933 | dependencies: 934 | "@babel/runtime" "^7.12.13" 935 | history "^4.9.0" 936 | hoist-non-react-statics "^3.1.0" 937 | loose-envify "^1.3.1" 938 | mini-create-react-context "^0.4.0" 939 | path-to-regexp "^1.7.0" 940 | prop-types "^15.6.2" 941 | react-is "^16.6.0" 942 | tiny-invariant "^1.0.2" 943 | tiny-warning "^1.0.0" 944 | 945 | react-transition-group@^4.4.2: 946 | version "4.4.2" 947 | resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.2.tgz#8b59a56f09ced7b55cbd53c36768b922890d5470" 948 | integrity sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg== 949 | dependencies: 950 | "@babel/runtime" "^7.5.5" 951 | dom-helpers "^5.0.1" 952 | loose-envify "^1.4.0" 953 | prop-types "^15.6.2" 954 | 955 | regenerator-runtime@^0.13.4: 956 | version "0.13.9" 957 | resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" 958 | integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== 959 | 960 | resolve-from@^4.0.0: 961 | version "4.0.0" 962 | resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" 963 | integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== 964 | 965 | resolve-pathname@^3.0.0: 966 | version "3.0.0" 967 | resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-3.0.0.tgz#99d02224d3cf263689becbb393bc560313025dcd" 968 | integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng== 969 | 970 | resolve-protobuf-schema@^2.1.0: 971 | version "2.1.0" 972 | resolved "https://registry.yarnpkg.com/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz#9ca9a9e69cf192bbdaf1006ec1973948aa4a3758" 973 | integrity sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ== 974 | dependencies: 975 | protocol-buffers-schema "^3.3.1" 976 | 977 | resolve@^1.12.0, resolve@^1.20.0: 978 | version "1.22.0" 979 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" 980 | integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== 981 | dependencies: 982 | is-core-module "^2.8.1" 983 | path-parse "^1.0.7" 984 | supports-preserve-symlinks-flag "^1.0.0" 985 | 986 | rollup@^2.59.0: 987 | version "2.66.0" 988 | resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.66.0.tgz#ee529ea15a20485d579039637fec3050bad03bbb" 989 | integrity sha512-L6mKOkdyP8HK5kKJXaiWG7KZDumPJjuo1P+cfyHOJPNNTK3Moe7zCH5+fy7v8pVmHXtlxorzaBjvkBMB23s98g== 990 | optionalDependencies: 991 | fsevents "~2.3.2" 992 | 993 | rw@^1.3.3: 994 | version "1.3.3" 995 | resolved "https://registry.yarnpkg.com/rw/-/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4" 996 | integrity sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q= 997 | 998 | safe-buffer@~5.1.1: 999 | version "5.1.2" 1000 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" 1001 | integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== 1002 | 1003 | sort-asc@^0.1.0: 1004 | version "0.1.0" 1005 | resolved "https://registry.yarnpkg.com/sort-asc/-/sort-asc-0.1.0.tgz#ab799df61fc73ea0956c79c4b531ed1e9e7727e9" 1006 | integrity sha1-q3md9h/HPqCVbHnEtTHtHp53J+k= 1007 | 1008 | sort-desc@^0.1.1: 1009 | version "0.1.1" 1010 | resolved "https://registry.yarnpkg.com/sort-desc/-/sort-desc-0.1.1.tgz#198b8c0cdeb095c463341861e3925d4ee359a9ee" 1011 | integrity sha1-GYuMDN6wlcRjNBhh45JdTuNZqe4= 1012 | 1013 | sort-object@^0.3.2: 1014 | version "0.3.2" 1015 | resolved "https://registry.yarnpkg.com/sort-object/-/sort-object-0.3.2.tgz#98e0d199ede40e07c61a84403c61d6c3b290f9e2" 1016 | integrity sha1-mODRme3kDgfGGoRAPGHWw7KQ+eI= 1017 | dependencies: 1018 | sort-asc "^0.1.0" 1019 | sort-desc "^0.1.1" 1020 | 1021 | source-map-js@^1.0.1: 1022 | version "1.0.2" 1023 | resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" 1024 | integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== 1025 | 1026 | source-map@^0.5.7: 1027 | version "0.5.7" 1028 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" 1029 | integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= 1030 | 1031 | stylis@4.0.13: 1032 | version "4.0.13" 1033 | resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.0.13.tgz#f5db332e376d13cc84ecfe5dace9a2a51d954c91" 1034 | integrity sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag== 1035 | 1036 | supports-color@^5.3.0: 1037 | version "5.5.0" 1038 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" 1039 | integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== 1040 | dependencies: 1041 | has-flag "^3.0.0" 1042 | 1043 | supports-preserve-symlinks-flag@^1.0.0: 1044 | version "1.0.0" 1045 | resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" 1046 | integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== 1047 | 1048 | threads@^1.7.0: 1049 | version "1.7.0" 1050 | resolved "https://registry.yarnpkg.com/threads/-/threads-1.7.0.tgz#d9e9627bfc1ef22ada3b733c2e7558bbe78e589c" 1051 | integrity sha512-Mx5NBSHX3sQYR6iI9VYbgHKBLisyB+xROCBGjjWm1O9wb9vfLxdaGtmT/KCjUqMsSNW6nERzCW3T6H43LqjDZQ== 1052 | dependencies: 1053 | callsites "^3.1.0" 1054 | debug "^4.2.0" 1055 | is-observable "^2.1.0" 1056 | observable-fns "^0.6.1" 1057 | optionalDependencies: 1058 | tiny-worker ">= 2" 1059 | 1060 | tiny-invariant@^1.0.2: 1061 | version "1.2.0" 1062 | resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.2.0.tgz#a1141f86b672a9148c72e978a19a73b9b94a15a9" 1063 | integrity sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg== 1064 | 1065 | tiny-warning@^1.0.0, tiny-warning@^1.0.3: 1066 | version "1.0.3" 1067 | resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" 1068 | integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== 1069 | 1070 | "tiny-worker@>= 2": 1071 | version "2.3.0" 1072 | resolved "https://registry.yarnpkg.com/tiny-worker/-/tiny-worker-2.3.0.tgz#715ae34304c757a9af573ae9a8e3967177e6011e" 1073 | integrity sha512-pJ70wq5EAqTAEl9IkGzA+fN0836rycEuz2Cn6yeZ6FRzlVS5IDOkFHpIoEsksPRQV34GDqXm65+OlnZqUSyK2g== 1074 | dependencies: 1075 | esm "^3.2.25" 1076 | 1077 | to-fast-properties@^2.0.0: 1078 | version "2.0.0" 1079 | resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" 1080 | integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= 1081 | 1082 | typescript@^4.3.5: 1083 | version "4.5.5" 1084 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.5.tgz#d8c953832d28924a9e3d37c73d729c846c5896f3" 1085 | integrity sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA== 1086 | 1087 | use-asset@^1.0.4: 1088 | version "1.0.4" 1089 | resolved "https://registry.yarnpkg.com/use-asset/-/use-asset-1.0.4.tgz#506caafc29f602890593799e58b577b70293a6e2" 1090 | integrity sha512-7/hqDrWa0iMnCoET9W1T07EmD4Eg/Wmoj/X8TGBc++ECRK4m5yTsjP4O6s0yagbxfqIOuUkIxe2/sA+VR2GxZA== 1091 | dependencies: 1092 | fast-deep-equal "^3.1.3" 1093 | 1094 | value-equal@^1.0.1: 1095 | version "1.0.1" 1096 | resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-1.0.1.tgz#1e0b794c734c5c0cade179c437d356d931a34d6c" 1097 | integrity sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw== 1098 | 1099 | vite@^2.7.10: 1100 | version "2.7.13" 1101 | resolved "https://registry.yarnpkg.com/vite/-/vite-2.7.13.tgz#99b56e27dfb1e4399e407cf94648f5c7fb9d77f5" 1102 | integrity sha512-Mq8et7f3aK0SgSxjDNfOAimZGW9XryfHRa/uV0jseQSilg+KhYDSoNb9h1rknOy6SuMkvNDLKCYAYYUMCE+IgQ== 1103 | dependencies: 1104 | esbuild "^0.13.12" 1105 | postcss "^8.4.5" 1106 | resolve "^1.20.0" 1107 | rollup "^2.59.0" 1108 | optionalDependencies: 1109 | fsevents "~2.3.2" 1110 | 1111 | webfont-matcher@^1.1.0: 1112 | version "1.1.0" 1113 | resolved "https://registry.yarnpkg.com/webfont-matcher/-/webfont-matcher-1.1.0.tgz#98ce95097b29e31fbe733053e10e571642d1c6c7" 1114 | integrity sha1-mM6VCXsp4x++czBT4Q5XFkLRxsc= 1115 | 1116 | xml-utils@^1.0.2: 1117 | version "1.0.2" 1118 | resolved "https://registry.yarnpkg.com/xml-utils/-/xml-utils-1.0.2.tgz#8081bfefb87b72e03e4adbabdd217ccbbc395eeb" 1119 | integrity sha512-rEn0FvKi+YGjv9omf22oAf+0d6Ly/sgJ/CUufU/nOzS7SRLmgwSujrewc03KojXxt+aPaTRpm593TgehtUBMSQ== 1120 | 1121 | yallist@^4.0.0: 1122 | version "4.0.0" 1123 | resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" 1124 | integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== 1125 | 1126 | yaml@^1.7.2: 1127 | version "1.10.2" 1128 | resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" 1129 | integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== 1130 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ 2 | module.exports = { 3 | preset: 'ts-jest', 4 | testEnvironment: 'jsdom', 5 | transformIgnorePatterns: ['/node_modules/(?!(ol|labelgun|mapbox-to-ol-style|ol-mapbox-style|geotiff)/).*/'], 6 | transform: { 7 | '^.+\\.js?$': require.resolve('babel-jest'), 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-ol-fiber", 3 | "version": "3.0.2", 4 | "description": "A React renderer for OpenLayers", 5 | "keywords": [ 6 | "react", 7 | "renderer", 8 | "fiber", 9 | "ol", 10 | "openlayers", 11 | "map", 12 | "maps" 13 | ], 14 | "author": "Giulio Zausa (https://github.com/giulioz)", 15 | "license": "MIT", 16 | "bugs": { 17 | "url": "https://github.com/giulioz/react-ol-fiber/issues" 18 | }, 19 | "homepage": "https://github.com/giulioz/react-ol-fiber#readme", 20 | "repository": { 21 | "type": "git", 22 | "url": "git+https://github.com/giulioz/react-ol-fiber.git" 23 | }, 24 | "main": "dist/index.cjs", 25 | "module": "dist/index.js", 26 | "types": "dist/index.d.ts", 27 | "sideEffects": false, 28 | "scripts": { 29 | "build": "rollup -c", 30 | "postbuild": "tsc --emitDeclarationOnly", 31 | "prepublishOnly": "npm run build", 32 | "eslint": "eslint src --fix --ext=js,ts,tsx,jsx", 33 | "typecheck": "tsc --emitDeclarationOnly", 34 | "test": "jest --coverage", 35 | "vers": "yarn changeset version", 36 | "release": "yarn build && yarn changeset publish", 37 | "changeset:add": "changeset add" 38 | }, 39 | "dependencies": { 40 | "react-reconciler": "^0.26.2", 41 | "scheduler": "^0.20.2", 42 | "utility-types": "^3.10.0" 43 | }, 44 | "devDependencies": { 45 | "@babel/core": "7.14.8", 46 | "@babel/plugin-proposal-class-properties": "^7.10.4", 47 | "@babel/plugin-transform-modules-commonjs": "7.15.0", 48 | "@babel/plugin-transform-parameters": "7.14.5", 49 | "@babel/plugin-transform-runtime": "7.15.0", 50 | "@babel/plugin-transform-template-literals": "7.14.5", 51 | "@babel/preset-env": "7.14.8", 52 | "@babel/preset-react": "7.14.5", 53 | "@babel/preset-typescript": "^7.14.5", 54 | "@changesets/changelog-git": "^0.1.7", 55 | "@changesets/cli": "^2.16.0", 56 | "@react-spring/core": "^9.4.2", 57 | "@rollup/plugin-babel": "^5.2.0", 58 | "@rollup/plugin-commonjs": "^20.0.0", 59 | "@rollup/plugin-json": "^4.1.0", 60 | "@rollup/plugin-node-resolve": "^13.0.4", 61 | "@types/jest": "^26.0.8", 62 | "@types/node": "^16.4.13", 63 | "@types/react": "^17.0.14", 64 | "@types/react-dom": "^17.0.9", 65 | "@types/react-reconciler": "^0.26.2", 66 | "@types/scheduler": "^0.16.2", 67 | "@typescript-eslint/eslint-plugin": "^4.28.4", 68 | "@typescript-eslint/parser": "^4.28.4", 69 | "babel-jest": "^27.3.1", 70 | "eslint": "^7.31.0", 71 | "eslint-config-prettier": "^8.3.0", 72 | "eslint-import-resolver-alias": "^1.1.2", 73 | "eslint-plugin-import": "^2.23.4", 74 | "eslint-plugin-jest": "^24.3.6", 75 | "eslint-plugin-prettier": "^3.4.0", 76 | "eslint-plugin-react": "^7.24.0", 77 | "eslint-plugin-react-hooks": "^4.2.0", 78 | "husky": "^7.0.1", 79 | "jest": "^27.0.6", 80 | "lint-staged": "^11.0.1", 81 | "ol": "^6.9.0", 82 | "prettier": "^2.3.2", 83 | "pretty-quick": "^3.1.1", 84 | "react": "^17.0.2", 85 | "react-dom": "^17.0.2", 86 | "rimraf": "^3.0.2", 87 | "rollup": "^2.26.10", 88 | "rollup-plugin-filesize": "^9.1.1", 89 | "rollup-plugin-terser": "^7.0.2", 90 | "ts-jest": "^27.0.4", 91 | "typescript": "^4.3.5" 92 | }, 93 | "peerDependencies": { 94 | "@react-spring/core": ">=9.4.2", 95 | "ol": ">=6.9.0", 96 | "react": ">=17.0", 97 | "react-dom": ">=17.0" 98 | }, 99 | "peerDependenciesMeta": { 100 | "react-dom": { 101 | "optional": true 102 | } 103 | }, 104 | "husky": { 105 | "hooks": { 106 | "pre-commit": "lint-staged" 107 | } 108 | }, 109 | "lint-staged": { 110 | "*.{js,jsx,ts,tsx}": [ 111 | "eslint --fix", 112 | "prettier --write" 113 | ] 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import babel from '@rollup/plugin-babel'; 3 | import resolve from '@rollup/plugin-node-resolve'; 4 | import json from '@rollup/plugin-json'; 5 | import { terser } from 'rollup-plugin-terser'; 6 | import filesize from 'rollup-plugin-filesize'; 7 | 8 | const root = process.platform === 'win32' ? path.resolve('/') : '/'; 9 | const external = id => !id.startsWith('.') && !id.startsWith(root); 10 | const extensions = ['.js', '.jsx', '.ts', '.tsx', '.json']; 11 | 12 | const getBabelOptions = ({ useESModules }, targets) => ({ 13 | babelrc: false, 14 | extensions, 15 | exclude: '**/node_modules/**', 16 | babelHelpers: 'runtime', 17 | presets: [['@babel/preset-env', { loose: true, modules: false, targets }], '@babel/preset-react', '@babel/preset-typescript'], 18 | plugins: [['@babel/transform-runtime', { regenerator: false, useESModules }]], 19 | }); 20 | 21 | export default [ 22 | { 23 | input: `./src/index.ts`, 24 | output: { file: `dist/index.js`, format: 'esm' }, 25 | external, 26 | plugins: [ 27 | json(), 28 | babel(getBabelOptions({ useESModules: true }, '>1%, not dead, not ie 11, not op_mini all')), 29 | resolve({ extensions }), 30 | terser(), 31 | filesize(), 32 | ], 33 | }, 34 | { 35 | input: `./src/index.ts`, 36 | output: { file: `dist/index.cjs`, format: 'cjs' }, 37 | external, 38 | plugins: [json(), babel(getBabelOptions({ useESModules: false })), resolve({ extensions }), terser(), filesize()], 39 | }, 40 | ]; 41 | -------------------------------------------------------------------------------- /src/MapComponent.tsx: -------------------------------------------------------------------------------- 1 | import React, { useRef, HTMLAttributes, useLayoutEffect, PropsWithChildren, createContext, ReactNode, useContext, forwardRef } from 'react'; 2 | 3 | import * as OL from 'ol'; 4 | // eslint-disable-next-line import/named 5 | import { ViewOptions } from 'ol/View'; 6 | import { fromLonLat } from 'ol/proj'; 7 | 8 | import { EventHandlerProps } from './ol-types'; 9 | import { reconciler, applyProps } from './reconciler'; 10 | 11 | interface RootState { 12 | map: OL.Map; 13 | } 14 | 15 | const context = createContext(null); 16 | const roots = new Map(); 17 | 18 | export function useOL() { 19 | const value = useContext(context); 20 | if (!value) { 21 | throw new Error('No context available!'); 22 | } 23 | return value; 24 | } 25 | 26 | interface MapProps { 27 | view?: OL.View | ViewOptions; 28 | } 29 | 30 | type MapEvents = Partial>; 31 | 32 | const defaultMapProps = { 33 | view: { 34 | center: fromLonLat([37.41, 8.82]), 35 | zoom: 4, 36 | }, 37 | }; 38 | 39 | export function render(element: ReactNode, rootDiv: HTMLDivElement, mapProps: MapProps & MapEvents) { 40 | const { view: viewProp, ...rest } = mapProps; 41 | const view = viewProp instanceof OL.View ? viewProp : { ...defaultMapProps.view, ...(viewProp || {}) }; 42 | 43 | const store = roots.get(rootDiv); 44 | let root = store?.root; 45 | const state = store?.state || { map: null }; 46 | 47 | if (!root) { 48 | state.map = new OL.Map({ 49 | target: rootDiv, 50 | layers: [], 51 | interactions: [], 52 | controls: [], 53 | view: new OL.View(view), 54 | }); 55 | 56 | root = reconciler.createContainer(state.map as any, 1, false, null); 57 | } 58 | 59 | if (state.map && mapProps.view instanceof OL.View && mapProps.view !== state.map.getView()) { 60 | state.map.setView(mapProps.view); 61 | } else if (mapProps.view && state.map && !(mapProps.view instanceof OL.View)) { 62 | applyProps(state.map.getView() as any, mapProps.view, {}); 63 | } 64 | 65 | if (state.map) { 66 | applyProps(state.map as any, rest as any, {}); 67 | } 68 | 69 | roots.set(rootDiv, { root, state: state as RootState }); 70 | 71 | reconciler.updateContainer({element}, root, null, () => undefined); 72 | 73 | return state; 74 | } 75 | 76 | export function unmountComponentAtNode(rootDiv: HTMLDivElement) { 77 | const store = roots.get(rootDiv); 78 | if (!store) return; 79 | 80 | const { root } = store; 81 | 82 | reconciler.updateContainer(null, root, null, () => { 83 | // TODO: cleanup map 84 | roots.delete(rootDiv); 85 | }); 86 | } 87 | 88 | export const MapComponent = forwardRef & MapProps & MapEvents>>(function MapComponent( 89 | { children, style, view, ...rest }, 90 | ref, 91 | ) { 92 | const mapDivRef = useRef(null); 93 | 94 | useLayoutEffect(() => { 95 | const state = render(children, mapDivRef.current!, { view, ...rest }); 96 | 97 | if (typeof ref === 'function') ref(state.map); 98 | else if (ref) ref.current = state.map; 99 | }, [children, view, ref, rest]); 100 | 101 | useLayoutEffect(() => { 102 | const container = mapDivRef.current!; 103 | return () => unmountComponentAtNode(container); 104 | }, []); 105 | 106 | return ; 107 | }); 108 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { useOL, unmountComponentAtNode, MapComponent } from './MapComponent'; 2 | export { extend } from './reconciler'; 3 | export { a } from './spring'; 4 | export * from './ol-types'; 5 | import * as ReactOlFiber from './ol-types'; 6 | export { ReactOlFiber }; 7 | -------------------------------------------------------------------------------- /src/ol-types.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import * as OL from 'ol'; 3 | import * as OLLayers from 'ol/layer'; 4 | import * as OLSources from 'ol/source'; 5 | import * as OLInteractions from 'ol/interaction'; 6 | import * as OLGeometries from 'ol/geom'; 7 | import * as OLStyles from 'ol/style'; 8 | import * as OLControls from 'ol/control'; 9 | import RenderEvent from 'ol/render/Event'; 10 | import { ObjectEvent } from 'ol/Object'; 11 | import { VectorSourceEvent } from 'ol/source/Vector'; 12 | import { TileSourceEvent } from 'ol/source/Tile'; 13 | import { ImageSourceEvent } from 'ol/source/Image'; 14 | 15 | export type StartingKeys = { 16 | [K in keyof T]: K extends `${Str}${Rest}` ? K : never; 17 | }[keyof T]; 18 | 19 | export type StripKey = K extends `${Str}${infer Rest}` ? Rest : never; 20 | 21 | export type AppendToKeys = keyof T extends string 22 | ? { 23 | [K in `${keyof T}${Str}`]: K extends `${infer S}${Str}` ? (S extends keyof T ? T[S] : never) : never; 24 | } 25 | : never; 26 | 27 | export type PrependToKeys = keyof T extends string 28 | ? { 29 | [K in `${Str}${keyof T}`]: K extends `${Str}${infer S}` ? (S extends keyof T ? T[S] : never) : never; 30 | } 31 | : never; 32 | 33 | export type AllSetters = Pick>; 34 | 35 | export type SettersValues = { 36 | [K in keyof T]: T[K] extends (value: infer VT) => unknown ? VT : never; 37 | }; 38 | 39 | export type SettableProps = { 40 | [K in Uncapitalize, 'set'>>]: `set${Capitalize}` extends keyof SettersValues 41 | ? SettersValues[`set${Capitalize}`] 42 | : never; 43 | }; 44 | 45 | export type Arg = T extends abstract new (...args: any) => any ? ConstructorParameters[0] : T; 46 | export type Args = T extends abstract new (...args: any) => any ? ConstructorParameters : T; 47 | 48 | export interface NodeProps { 49 | attach?: string; 50 | attachAdd?: string; 51 | arg?: Omit, 'prototype'>; 52 | args?: Omit, 'prototype'>; 53 | children?: React.ReactNode; 54 | ref?: React.Ref; 55 | key?: React.Key; 56 | onUpdate?: (self: T) => void; 57 | } 58 | 59 | export type PrimitiveType = Omit, 'arg' | 'args'> & { object: T | (() => T) }; 60 | 61 | export type FnType = Omit, 'arg' | 'args'> & { fn: (...params: TP) => TR }; 62 | 63 | type EventHandlerKeys = T extends { 64 | on: (keys: [infer K], ...args: any) => any; 65 | } 66 | ? K extends string 67 | ? K 68 | : never 69 | : never; 70 | 71 | type ListenerTypeForKey = T extends { 72 | on: (keys: K, listener: (ev: infer L) => infer R) => any; 73 | } 74 | ? (ev: Omit & { target: T }) => R 75 | : never; 76 | 77 | type KnownEventFix = K extends `Pointer${infer Rest}` 78 | ? (e: OL.MapBrowserEvent) => void 79 | : K extends `${infer Rest}click` 80 | ? (e: OL.MapBrowserEvent) => void 81 | : K extends `${infer Rest}compose` 82 | ? (e: RenderEvent) => void 83 | : K extends `${infer Rest}render` 84 | ? (e: RenderEvent) => void 85 | : K extends `${infer Rest}feature` 86 | ? (e: VectorSourceEvent) => void 87 | : K extends `Featuresload${infer Rest}` 88 | ? (e: VectorSourceEvent) => void 89 | : K extends `Tileload${infer Rest}` 90 | ? (e: TileSourceEvent) => void 91 | : K extends `Imageload${infer Rest}` 92 | ? (e: ImageSourceEvent) => void 93 | : K extends `Change:${infer Rest}` 94 | ? (e: ObjectEvent) => void 95 | : K extends `Propertychange` 96 | ? (e: ObjectEvent) => void 97 | : K extends `Move${infer Rest}` 98 | ? (e: OL.MapEvent) => void 99 | : ListenerTypeForKey>; 100 | 101 | export type EventHandlerProps = PrependToKeys< 102 | { 103 | [key in Capitalize>]: KnownEventFix; 104 | }, 105 | 'on' 106 | >; 107 | 108 | export type Node = Partial> & Partial> & NodeProps; 109 | 110 | export type TypeOLCustomClass = Node, T>; 111 | 112 | type ConstructedObject = T extends new (...args: any) => infer C ? C : never; 113 | 114 | type ExtractIntrinsicElements = keyof T extends string 115 | ? { 116 | [K in Uncapitalize]: Capitalize extends keyof T ? Node]>, T[Capitalize]> : never; 117 | } 118 | : never; 119 | 120 | type BaseElements = ExtractIntrinsicElements; 121 | type LayerElements = AppendToKeys, 'Layer'>; 122 | type SourceElements = AppendToKeys, 'Source'>; 123 | type InteractionElements = AppendToKeys, 'Interaction'>; 124 | type GeometryElements = AppendToKeys, 'Geometry'>; 125 | type StyleElements = AppendToKeys, 'Style'>; 126 | type ControlElements = AppendToKeys, 'Control'>; 127 | 128 | export type AllElements = BaseElements & 129 | LayerElements & 130 | SourceElements & 131 | InteractionElements & 132 | GeometryElements & 133 | StyleElements & 134 | ControlElements & { 135 | olPrimitive: PrimitiveType; 136 | olFn: FnType; 137 | olView: Node, typeof OL['View']>; 138 | }; 139 | 140 | declare global { 141 | // eslint-disable-next-line @typescript-eslint/no-namespace 142 | namespace JSX { 143 | interface IntrinsicElements extends AllElements {} 144 | } 145 | } 146 | 147 | // Allows correct props for a primitive with generics 148 | export function OLPrimitive(props: Omit, 'arg' | 'args'> & { object: T }) { 149 | return ; 150 | } 151 | 152 | // Allows correct props for a function with generics 153 | export function OLFn(props: Omit, 'arg' | 'args'> & { fn: (...params: TP) => TR }) { 154 | return ; 155 | } 156 | -------------------------------------------------------------------------------- /src/reconciler.ts: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Reconciler from 'react-reconciler'; 3 | import { unstable_now as now } from 'scheduler'; 4 | 5 | import * as OL from 'ol'; 6 | import * as OLLayers from 'ol/layer'; 7 | import * as OLSources from 'ol/source'; 8 | import * as OLInteractions from 'ol/interaction'; 9 | import * as OLGeometries from 'ol/geom'; 10 | import * as OLStyles from 'ol/style'; 11 | import * as OLControls from 'ol/control'; 12 | 13 | import { pascalCase, pruneKeys, shallowCompare } from './utils'; 14 | 15 | type GenericOLInstance = { 16 | dispose?: () => void; 17 | changed?: () => void; 18 | getVisible?: () => boolean; 19 | setVisible?: (v: boolean) => void; 20 | get?: (key: string) => unknown; 21 | set?: (key: string, value: unknown) => void; 22 | on(ev: string, handler: (...args: any) => void): void; 23 | un(ev: string, handler: (...args: any) => void): void; 24 | attach?: string; 25 | attachAdd?: string; 26 | _root?: any; 27 | _parent?: GenericOLInstance; 28 | _primitive?: boolean; 29 | _type?: string; 30 | _attached?: { key: string; value: GenericOLInstance }[]; 31 | _attachedAdd?: { key: string; value: GenericOLInstance }[]; 32 | } & { [k in `get${Capitalize}`]: () => any } & { 33 | [k in `set${Capitalize}`]: (value: any) => void; 34 | } & { 35 | [k in `add${Capitalize}`]: (value: any) => void; 36 | } & { [k in `remove${Capitalize}`]: (value: any) => GenericOLInstance | null } & { 37 | [k in `has${Capitalize}`]: (value: any) => boolean; 38 | }; 39 | 40 | interface Catalogue { 41 | [name: string]: 42 | | { new (...args: any): GenericOLInstance } 43 | | { 44 | [k: string]: { new (...args: any): GenericOLInstance } | ((...args: any) => OL.Collection); 45 | }; 46 | } 47 | export let catalogue: Catalogue = { 48 | '*Layer': OLLayers as any, 49 | '*Source': OLSources as any, 50 | '*Interaction': OLInteractions as any, 51 | '*Geometry': OLGeometries as any, 52 | '*Style': OLStyles as any, 53 | '*Control': OLControls as any, 54 | olView: OL.View as any, 55 | }; 56 | 57 | export const extend = (objects: Catalogue): void => void (catalogue = { ...catalogue, ...objects }); 58 | 59 | function getConstructor(name: string) { 60 | const candidateEnding = Object.keys(catalogue) 61 | .find(k => k.startsWith('*') && name.endsWith(k.substring(1))) 62 | ?.substring(1); 63 | const namespacedCatalogueEntry = 64 | candidateEnding && (catalogue as any)[`*${candidateEnding}`][pascalCase(name.substring(0, name.length - candidateEnding.length))]; 65 | 66 | const directCatalogueEntry = (catalogue[name] || catalogue[pascalCase(name)]) as { 67 | new (...args: any): GenericOLInstance; 68 | }; 69 | 70 | return directCatalogueEntry || namespacedCatalogueEntry || (OL as any)[pascalCase(name)]; 71 | } 72 | 73 | function getGetter(instance: GenericOLInstance, prop: TK) { 74 | return instance[`get${pascalCase(prop)}` as const]?.bind(instance) || (instance.get && (() => instance.get && instance.get(prop))); 75 | } 76 | function getGetterArray(instance: GenericOLInstance, prop: TK) { 77 | const getter = getGetter(instance, `${prop}s`); 78 | if (!getter) return null; 79 | else { 80 | return () => { 81 | const array = getter(); 82 | return array.array_ ? array.array_ : array; 83 | }; 84 | } 85 | } 86 | function getSetter(instance: GenericOLInstance, prop: TK) { 87 | return instance[`set${pascalCase(prop)}` as const]?.bind(instance) || (instance.set && ((value: any) => instance.set && instance.set(prop, value))) || null; 88 | } 89 | function getSetterArray(instance: GenericOLInstance, prop: TK) { 90 | return instance[`set${pascalCase(prop)}s` as const]?.bind(instance) || null; 91 | } 92 | function getAppender(instance: GenericOLInstance, prop: TK) { 93 | return instance[`add${pascalCase(prop)}` as const]?.bind(instance) || null; 94 | } 95 | function getRemover(instance: GenericOLInstance, prop: TK) { 96 | return instance[`remove${pascalCase(prop)}` as const]?.bind(instance) || null; 97 | } 98 | function getHaser(instance: GenericOLInstance, prop: TK) { 99 | const getter = getGetter(instance, `${prop}s`); 100 | if (!getter) return () => false; 101 | const array = getter(); 102 | const arrayChecker = array?.includes?.bind(array) || array?.array_?.includes?.bind(array.array_); 103 | return instance[`has${pascalCase(prop)}` as const]?.bind(instance) || ((value: any) => arrayChecker(value)) || (() => false); 104 | } 105 | 106 | export function applyProps( 107 | instance: GenericOLInstance, 108 | newProps: Record, 109 | oldProps?: Record, 110 | diff?: string[], 111 | forceReplace?: boolean, 112 | ) { 113 | // Filter identical props and reserved keys 114 | const identical = Object.keys(newProps).filter(key => newProps[key] === oldProps?.[key]); 115 | const props = pruneKeys(newProps, [...identical, 'children', 'key', 'ref']); 116 | 117 | // Mutate our OL element 118 | if (Object.keys(props).length) { 119 | Object.entries(props).forEach(([key, value]) => { 120 | if (key.startsWith('attach')) { 121 | instance[key as 'attach'] = value; 122 | } else if (key.startsWith('on')) { 123 | const eventKey = key.substring(2).toLowerCase(); 124 | if (oldProps?.[key]) instance.un(eventKey, oldProps[key] as () => void); 125 | instance.on(eventKey, value); 126 | } else { 127 | const setter = getSetter(instance, key); 128 | if (setter) { 129 | setter(value); 130 | } else { 131 | // No setter, must recreate! 132 | if (forceReplace && instance._type && instance._parent) { 133 | const newInstance = createInstance(instance._type, newProps); 134 | removeChild(instance._parent, instance); 135 | appendChild(instance._parent, newInstance); 136 | } 137 | } 138 | } 139 | }); 140 | } 141 | } 142 | 143 | function triggerParentChanged(instance?: GenericOLInstance) { 144 | if (instance && instance.changed) { 145 | instance.changed(); 146 | } else if (instance && instance._parent) { 147 | triggerParentChanged(instance._parent); 148 | } 149 | } 150 | 151 | function appendChild(parentInstance: GenericOLInstance, child: GenericOLInstance) { 152 | if (!child) return; 153 | 154 | if (child.attach) { 155 | const setter = getSetter(parentInstance, child.attach); 156 | if (setter) { 157 | setter(child); 158 | parentInstance._attached = parentInstance._attached || []; 159 | parentInstance._attached.push({ key: child.attach, value: child }); 160 | } else { 161 | // No setter, must recreate! 162 | } 163 | } else if (child.attachAdd) { 164 | const appender = getAppender(parentInstance, child.attachAdd); 165 | if (appender && !getHaser(parentInstance, child.attachAdd)(child)) { 166 | appender(child); 167 | parentInstance._attachedAdd = parentInstance._attachedAdd || []; 168 | parentInstance._attachedAdd.push({ key: child.attachAdd, value: child }); 169 | } else { 170 | const getter = getGetter(parentInstance, child.attachAdd); 171 | const setter = getSetter(parentInstance, child.attachAdd); 172 | if (getter && setter) { 173 | const val = getter(); 174 | const arr = Array.isArray(val) ? val : []; 175 | setter([...arr, child]); 176 | } 177 | } 178 | } 179 | 180 | child._parent = parentInstance || null; 181 | child._root = parentInstance._root || parentInstance; 182 | 183 | triggerParentChanged(parentInstance); 184 | } 185 | 186 | function removeChild(parentInstance: GenericOLInstance, child: GenericOLInstance) { 187 | if (!child) return; 188 | 189 | let after: GenericOLInstance | null = null; 190 | 191 | if (child.attach) { 192 | const setter = getSetter(parentInstance, child.attach); 193 | if (setter) { 194 | setter(null); 195 | parentInstance._attached = parentInstance._attached || []; 196 | parentInstance._attached.splice( 197 | parentInstance._attached.findIndex(e => e.key === child.attach && e.value === child), 198 | 1, 199 | ); 200 | } else { 201 | // No setter, must recreate! 202 | } 203 | } else if (child.attachAdd && getHaser(parentInstance, child.attachAdd)(child)) { 204 | const remover = getRemover(parentInstance, child.attachAdd); 205 | if (remover) { 206 | const getter = getGetterArray(parentInstance, child.attachAdd); 207 | const arr = getter && (getter().getArray ? getter().getArray() : (getter() as any[])); 208 | const elementIndex = arr ? arr.indexOf(child) : -1; 209 | after = arr && arr[elementIndex - 1]; 210 | 211 | remover(child); 212 | parentInstance._attachedAdd = parentInstance._attachedAdd || []; 213 | parentInstance._attachedAdd.splice( 214 | parentInstance._attachedAdd.findIndex(e => e.key === child.attachAdd && e.value === child), 215 | 1, 216 | ); 217 | } 218 | } 219 | 220 | if (child.dispose) { 221 | child.dispose(); 222 | } 223 | 224 | return after; 225 | } 226 | 227 | function insertBefore(parentInstance: GenericOLInstance, child: GenericOLInstance, beforeChild: GenericOLInstance) { 228 | if (!child) return; 229 | 230 | child._parent = parentInstance; 231 | const getter = child.attachAdd && getGetterArray(parentInstance, child.attachAdd); 232 | const setter = child.attachAdd && getSetterArray(parentInstance, child.attachAdd); 233 | if (getter && setter) { 234 | const children = getter() as GenericOLInstance[]; 235 | const index = children.indexOf(beforeChild); 236 | const newChildren = [...children.slice(0, index), child, ...children.slice(index)]; 237 | setter(newChildren); 238 | } else { 239 | appendChild(parentInstance, child); 240 | } 241 | } 242 | 243 | function autoAttach(name: string, props: any, object?: any) { 244 | if (props.attach || props.attachAdd) return {}; 245 | 246 | if (name.endsWith('Layer') || object instanceof OLLayers.Layer) { 247 | return { attachAdd: 'layer' }; 248 | } else if (name.endsWith('Interaction') || object instanceof OLInteractions.Interaction) { 249 | return { attachAdd: 'interaction' }; 250 | } else if (name.endsWith('Geometry') || object instanceof OLGeometries.Geometry) { 251 | return { attach: 'geometry' }; 252 | } else if (name === 'fillStyle' || name === 'strokeStyle') { 253 | return { attach: name.substring(0, name.length - 'Style'.length).toLowerCase() }; 254 | } else if (name === 'styleStyle' || object instanceof OLStyles.Style) { 255 | return { attach: 'style' }; 256 | } else if (name === 'textStyle' || object instanceof OLStyles.Text) { 257 | return { attach: 'text' }; 258 | } else if (name.endsWith('Style') || object instanceof OLStyles.Image) { 259 | return { attach: 'image' }; 260 | } else if (name.endsWith('Source') || object instanceof OLSources.Source) { 261 | return { attach: 'source' }; 262 | } else if (name.endsWith('Control' || object instanceof OLControls.Control)) { 263 | return { attachAdd: 'control' }; 264 | } else if (name.toLowerCase().endsWith('view') || object instanceof OL.View) { 265 | return { attach: 'view' }; 266 | } else if (name.toLowerCase().endsWith('feature') || object instanceof OL.Feature) { 267 | return { attachAdd: 'feature' }; 268 | } else if (name === 'olPrimitive') { 269 | return { _primitive: true }; 270 | } 271 | return {}; 272 | } 273 | 274 | function getImmutableChildren(type: string, children: any): false | { type: string; props: any }[] { 275 | const target = getConstructor(type); 276 | if (target && children) { 277 | const childrenArr: { type: string; props: any }[] = Array.isArray(children) ? children : [children]; 278 | const unappliableChildren = childrenArr.filter(child => { 279 | const attachProps = child && typeof child.type === 'string' && { ...autoAttach(child.type, child.props), ...child.props }; 280 | const setter = attachProps?.attach && target.prototype[`set${pascalCase(attachProps.attach)}`]; 281 | const adder = attachProps?.attachAdd && target.prototype[`add${pascalCase(attachProps.attachAdd)}`]; 282 | return child && attachProps && (!(setter || adder) || getImmutableChildren(child.type, child.props.children)); 283 | }); 284 | 285 | if (unappliableChildren.length > 0) { 286 | return unappliableChildren; 287 | } 288 | } 289 | 290 | return false; 291 | } 292 | 293 | // Hacky mini renderer for style functions 294 | function miniChildCreate(jsx: JSX.Element) { 295 | if (!jsx) return null; 296 | const root = createInstance(jsx.type, jsx.props); 297 | const children: JSX.Element[] = Array.isArray(jsx.props.children) ? jsx.props.children : [jsx.props.children]; 298 | const childrenCreated = children.map(miniChildCreate); 299 | childrenCreated.forEach(c => appendChild(root, c)); 300 | return root; 301 | } 302 | 303 | function fnWrapper(fn: Function) { 304 | return (...params: any[]) => { 305 | const jsx = fn(...params); 306 | if (React.isValidElement(jsx)) { 307 | return miniChildCreate(jsx); 308 | } else { 309 | return jsx; 310 | } 311 | }; 312 | } 313 | 314 | function createInstance(type: string, { object, arg, args, fn, ...props }: any) { 315 | const target = getConstructor(type); 316 | 317 | if (type !== 'olPrimitive' && type !== 'olFn' && !target) throw new Error(`${type} is not a part of the OL namespace.`); 318 | if (type === 'olPrimitive' && !object) throw new Error(`"object" must be set when using primitives.`); 319 | if (type === 'olFn' && !fn) throw new Error(`"fn" must be a function when using olFn.`); 320 | 321 | props = { ...autoAttach(type, props, object), ...props }; 322 | 323 | const unappliableChildren = getImmutableChildren(type, props.children); 324 | if (unappliableChildren) { 325 | const childrenArgs: any = {}; 326 | unappliableChildren.forEach(c => { 327 | const a = c && typeof c.type === 'string' && { ...autoAttach(c.type, c.props), ...c.props }; 328 | if (a.attach && c) { 329 | childrenArgs[a.attach] = createInstance(c.type, c.props); 330 | } 331 | }); 332 | 333 | arg = arg || {}; 334 | arg = { ...arg, ...childrenArgs }; 335 | } 336 | 337 | const instance = 338 | (fn instanceof Function && fnWrapper(fn)) || 339 | (object instanceof Function ? object() : object) || 340 | (Array.isArray(args) ? new target(...args) : new target(arg)); 341 | instance._type = type; 342 | 343 | applyProps(instance, props, {}, []); 344 | return instance; 345 | } 346 | 347 | function switchInstance(instance: GenericOLInstance, type: string, newProps: any, fiber: Reconciler.Fiber) { 348 | const parent = instance._parent; 349 | if (!parent) return; 350 | const newInstance = createInstance(type, newProps); 351 | 352 | const after = removeChild(parent, instance); 353 | if (after) { 354 | insertBefore(parent, newInstance, after); 355 | } else { 356 | appendChild(parent, newInstance); 357 | } 358 | 359 | (instance._attached || []).forEach(a => { 360 | appendChild(newInstance, a.value); 361 | }); 362 | (instance._attachedAdd || []).forEach(a => { 363 | appendChild(newInstance, a.value); 364 | }); 365 | 366 | // This evil hack switches the react-internal fiber node 367 | // https://github.com/facebook/react/issues/14983 368 | // https://github.com/facebook/react/pull/15021 369 | [fiber, fiber.alternate].forEach(newFiber => { 370 | if (newFiber !== null) { 371 | newFiber.stateNode = newInstance; 372 | if (newFiber.ref) { 373 | if (typeof newFiber.ref === 'function') (newFiber as unknown as any).ref(newInstance); 374 | else (newFiber.ref as Reconciler.RefObject).current = newInstance; 375 | } 376 | } 377 | }); 378 | } 379 | 380 | export const reconciler = Reconciler({ 381 | // OL objects can be updated, so we inform the renderer 382 | supportsMutation: true, 383 | 384 | // We set this to false because this can work on top of react-dom 385 | isPrimaryRenderer: false, 386 | 387 | // We can modify the ref here, but we return it instead (no-op) 388 | getPublicInstance: instance => instance, 389 | 390 | // This object that's passed into the reconciler is the host context. 391 | // We don't need to expose it though 392 | getRootHostContext: () => ({}), 393 | getChildHostContext: () => ({}), 394 | 395 | // Text isn't supported in OL, so we skip it 396 | createTextInstance: () => {}, 397 | 398 | // This is used to calculate updates in the render phase or commitUpdate 399 | prepareUpdate(instance: GenericOLInstance, type: string, oldProps: any, newProps: any) { 400 | if (getImmutableChildren(type, newProps.children)) return [true]; 401 | if (instance._primitive && newProps.object && newProps.object !== instance) return [true]; 402 | else { 403 | // This is a data object, let's extract critical information about it 404 | const { arg: argNew, args: argsNew = [], children: cN, ...restNew } = newProps; 405 | const { arg: argOld, args: argsOld = [], children: cO, ...restOld } = oldProps; 406 | // If it has new props or arguments, then it needs to be re-instanciated 407 | if (!shallowCompare(argsNew, argsOld) || !shallowCompare(argNew, argOld)) return [true]; 408 | // Create a diff-set, flag if there are any changes 409 | const diff = Object.entries(restNew) 410 | .filter(([key, value]) => !shallowCompare(value, restOld[key])) 411 | .map(([key]) => key); 412 | if (diff.length) return [false, ...diff]; 413 | // Otherwise do not touch the instance 414 | return null; 415 | } 416 | }, 417 | 418 | // This lets us store stuff before React mutates our OL objects. 419 | // We don't do anything here but return an empty object 420 | prepareForCommit: () => null, 421 | resetAfterCommit: () => ({}), 422 | 423 | // OL elements don't have textContent, so we skip this 424 | shouldSetTextContent: () => false, 425 | 426 | // We can mutate objects once they're assembled into the scene graph here. 427 | // applyProps removes the need for this though 428 | finalizeInitialChildren: () => false, 429 | 430 | // This can modify the container and clear children. 431 | // Might be useful for disposing on demand later 432 | clearContainer: () => false, 433 | 434 | // This is where we'll create a OL element from a React element 435 | createInstance, 436 | 437 | // These methods add elements to the scene 438 | appendChild, 439 | appendInitialChild: appendChild, 440 | appendChildToContainer: appendChild, 441 | 442 | // These methods remove elements from the scene 443 | removeChild, 444 | removeChildFromContainer: removeChild, 445 | 446 | insertBefore, 447 | 448 | insertInContainerBefore: (parentInstance: GenericOLInstance, child: GenericOLInstance, beforeChild: GenericOLInstance) => 449 | insertBefore( 450 | // getContainer(parentInstance, child).container, 451 | parentInstance, 452 | child, 453 | beforeChild, 454 | ), 455 | 456 | // This is where we mutate OL objects in the render phase 457 | commitUpdate( 458 | instance: GenericOLInstance, 459 | [reconstruct, ...diff]: [boolean, ...string[]], 460 | type, 461 | oldProps: Record, 462 | newProps: Record, 463 | fiber: Reconciler.Fiber, 464 | ) { 465 | // Reconstruct when args or ) { 475 | if (props.visible == null || props.visible) { 476 | instance.setVisible?.(true); 477 | } 478 | }, 479 | hideTextInstance() {}, 480 | 481 | supportsPersistence: false, 482 | supportsHydration: false, 483 | preparePortalMount() {}, 484 | now, 485 | scheduleTimeout: setTimeout, 486 | cancelTimeout: clearTimeout, 487 | noTimeout: -1, 488 | }); 489 | -------------------------------------------------------------------------------- /src/spring.ts: -------------------------------------------------------------------------------- 1 | import { Globals } from '@react-spring/core'; 2 | import { createStringInterpolator, colors } from '@react-spring/shared'; 3 | import { createHost } from '@react-spring/animated'; 4 | 5 | import { applyProps, catalogue } from './reconciler'; 6 | import { AllElements } from './ol-types'; 7 | 8 | type Primitives = keyof AllElements; 9 | 10 | const primitives = Object.entries(catalogue) 11 | .flatMap(([key, value]) => { 12 | if (key.startsWith('*')) { 13 | return Object.keys(value).map(k => k + key.substring(1)); 14 | } else { 15 | return key; 16 | } 17 | }) 18 | .filter(key => /^[A-Z]/.test(key) || key.startsWith('ol')) 19 | .map(key => key[0].toLowerCase() + key.slice(1)) as Primitives[]; 20 | 21 | Globals.assign({ 22 | createStringInterpolator, 23 | colors, 24 | }); 25 | 26 | const host = createHost(primitives, { 27 | applyAnimatedValues: (instance, props) => applyProps(instance, props, undefined, undefined, true), 28 | }); 29 | 30 | export const animated = host.animated; 31 | export { animated as a }; 32 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | export function shallowCompare(a: any, b: any) { 2 | if (a === b) return true; 3 | if (Array.isArray(a) && Array.isArray(b)) return a.every((e, i) => b[i] === e); 4 | if (typeof a === 'object' && typeof b === 'object') { 5 | const keys = Array.from(new Set([...Object.keys(a), ...Object.keys(b)])); 6 | return keys.every(key => a[key] === b[key]); 7 | } 8 | } 9 | 10 | export const pascalCase = (str: T) => (str.charAt(0).toUpperCase() + str.substring(1)) as Capitalize; 11 | 12 | export function pruneKeys(obj: T, ...keys: string[][]) { 13 | const keysToRemove = new Set(keys.flat()); 14 | 15 | return Object.fromEntries(Object.entries(obj).filter(([key]) => !keysToRemove.has(key))); 16 | } 17 | -------------------------------------------------------------------------------- /tests/attachments.test.tsx: -------------------------------------------------------------------------------- 1 | import { miniRender } from './utils'; 2 | import { extend } from '../src'; 3 | import React, { PropsWithChildren, useState } from 'react'; 4 | 5 | import { Fill, Stroke, Style } from 'ol/style'; 6 | import CircleStyle from 'ol/style/Circle'; 7 | import TextStyle from 'ol/style/Text'; 8 | import { Circle, Point, Polygon } from 'ol/geom'; 9 | import { DragPan, MouseWheelZoom } from 'ol/interaction'; 10 | import VectorLayer from 'ol/layer/Vector'; 11 | import TileLayer from 'ol/layer/Tile'; 12 | import ImageLayer from 'ol/layer/Image'; 13 | import VectorSource from 'ol/source/Vector'; 14 | import { Feature } from 'ol'; 15 | import { Attribution, Zoom } from 'ol/control'; 16 | import BaseObject from 'ol/Object'; 17 | import Static from 'ol/source/ImageStatic'; 18 | 19 | class UnknownCustom extends BaseObject {} 20 | extend({ UnknownCustom: UnknownCustom as any }); 21 | declare global { 22 | namespace JSX { 23 | interface IntrinsicElements { 24 | unknownCustom: any; 25 | } 26 | } 27 | } 28 | 29 | describe('Static attachments', () => { 30 | test('correctly attaches a single layer to a map (implicit)', async () => { 31 | const [_, map] = await miniRender(); 32 | expect(map.getLayers().getLength()).toBe(1); 33 | expect(map.getLayers().getArray()[0]).toBeInstanceOf(VectorLayer); 34 | }); 35 | 36 | test('correctly attaches a single layer to a map (explicit)', async () => { 37 | const [_, map] = await miniRender(); 38 | expect(map.getLayers().getLength()).toBe(1); 39 | expect(map.getLayers().getArray()[0]).toBeInstanceOf(TileLayer); 40 | }); 41 | 42 | test('correctly attaches multiple layers to a map in order', async () => { 43 | const [_, map] = await miniRender( 44 | <> 45 | 46 | 47 | 48 | >, 49 | ); 50 | expect(map.getLayers().getLength()).toBe(3); 51 | expect(map.getLayers().getArray()[0]).toBeInstanceOf(VectorLayer); 52 | expect(map.getLayers().getArray()[1]).toBeInstanceOf(TileLayer); 53 | expect(map.getLayers().getArray()[2]).toBeInstanceOf(ImageLayer); 54 | }); 55 | 56 | test('correctly attaches multiple layers to a map in order from a component', async () => { 57 | function MyLayers() { 58 | return ( 59 | <> 60 | 61 | 62 | > 63 | ); 64 | } 65 | 66 | const [_, map] = await miniRender( 67 | <> 68 | 69 | 70 | >, 71 | ); 72 | expect(map.getLayers().getLength()).toBe(3); 73 | expect(map.getLayers().getArray()[0]).toBeInstanceOf(VectorLayer); 74 | expect(map.getLayers().getArray()[1]).toBeInstanceOf(TileLayer); 75 | expect(map.getLayers().getArray()[2]).toBeInstanceOf(ImageLayer); 76 | }); 77 | 78 | test('correctly attaches a style to a layer (implicit)', async () => { 79 | const [_, map] = await miniRender( 80 | 81 | 82 | , 83 | ); 84 | const layer = map.getLayers().getArray()[0] as VectorLayer; 85 | expect(layer.getStyle()).toBeInstanceOf(Style); 86 | }); 87 | 88 | test('correctly attaches a style to a layer (explicit)', async () => { 89 | const [_, map] = await miniRender( 90 | 91 | 92 | , 93 | ); 94 | const layer = map.getLayers().getArray()[0] as VectorLayer; 95 | expect(layer.getStyle()).toBeInstanceOf(Style); 96 | }); 97 | 98 | test('correctly attaches a feature with style and geometry to a source to a layer', async () => { 99 | const [_, map] = await miniRender( 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | , 108 | ); 109 | const layer = map.getLayers().getArray()[0] as VectorLayer>; 110 | expect(layer.getSource()).toBeInstanceOf(VectorSource); 111 | expect(layer.getSource().getFeatures()).toHaveLength(1); 112 | expect(layer.getSource().getFeatures()[0].getStyle()).toBeInstanceOf(Style); 113 | expect(layer.getSource().getFeatures()[0].getGeometry()).toBeInstanceOf(Point); 114 | }); 115 | 116 | test('correctly attaches multiple features in order', async () => { 117 | const [_, map] = await miniRender( 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | , 131 | ); 132 | const layer = map.getLayers().getArray()[0] as VectorLayer>; 133 | expect(layer.getSource().getFeatures()).toHaveLength(3); 134 | expect(layer.getSource().getFeatures()[0].getGeometry()).toBeInstanceOf(Point); 135 | expect(layer.getSource().getFeatures()[1].getGeometry()).toBeInstanceOf(Circle); 136 | expect(layer.getSource().getFeatures()[2].getGeometry()).toBeInstanceOf(Polygon); 137 | const circle = layer.getSource().getFeatures()[1].getGeometry() as Circle; 138 | expect(circle.getRadius()).toBe(4); 139 | expect(circle.getCenter()).toEqual([2, 5]); 140 | }); 141 | 142 | test('correctly attaches multiple interactions', async () => { 143 | const [_, map] = await miniRender( 144 | <> 145 | 146 | 147 | >, 148 | ); 149 | expect(map.getInteractions().getArray()).toHaveLength(2); 150 | expect(map.getInteractions().getArray()[0]).toBeInstanceOf(DragPan); 151 | expect(map.getInteractions().getArray()[1]).toBeInstanceOf(MouseWheelZoom); 152 | }); 153 | 154 | test('correctly attaches multiple controls', async () => { 155 | const [_, map] = await miniRender( 156 | <> 157 | 158 | 159 | >, 160 | ); 161 | expect(map.getControls().getArray()).toHaveLength(2); 162 | expect(map.getControls().getArray()[0]).toBeInstanceOf(Zoom); 163 | expect(map.getControls().getArray()[1]).toBeInstanceOf(Attribution); 164 | }); 165 | 166 | test('handles attachment of an invalid element with extend', async () => { 167 | let refVal = null as any; 168 | function TestComponent() { 169 | return (refVal = e)} />; 170 | } 171 | await miniRender(); 172 | expect(refVal).toBeInstanceOf(UnknownCustom); 173 | expect(refVal.attach).toBeFalsy(); 174 | expect(refVal.attachAdd).toBeFalsy(); 175 | }); 176 | 177 | test('correctly attaches fill and stroke to a style', async () => { 178 | const [_, map] = await miniRender( 179 | 180 | 181 | 182 | 183 | 184 | , 185 | ); 186 | const layer = map.getLayers().getArray()[0] as VectorLayer>; 187 | expect(layer.getStyle()).toBeInstanceOf(Style); 188 | const style = layer.getStyle() as Style; 189 | expect(style.getFill()).toBeInstanceOf(Fill); 190 | expect(style.getFill().getColor()).toBe('red'); 191 | expect(style.getStroke()).toBeInstanceOf(Stroke); 192 | expect(style.getStroke().getWidth()).toBe(4); 193 | }); 194 | 195 | test('correctly attaches multiple styles', async () => { 196 | const [_, map] = await miniRender( 197 | 198 | {/* TODO: support auto attachAdd with multiple children */} 199 | 200 | 201 | 202 | 203 | 204 | 205 | , 206 | ); 207 | const layer = map.getLayers().getArray()[0] as VectorLayer>; 208 | expect(Array.isArray(layer.getStyle())).toBeTruthy(); 209 | const styles = layer.getStyle() as Style[]; 210 | expect(styles[0].getFill().getColor()).toBe('red'); 211 | expect(styles[1].getFill().getColor()).toBe('blue'); 212 | }); 213 | 214 | test('correctly attaches a circle to a style (children as ctor params)', async () => { 215 | const [_, map] = await miniRender( 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | , 224 | ); 225 | const layer = map.getLayers().getArray()[0] as VectorLayer>; 226 | expect(layer.getStyle()).toBeInstanceOf(Style); 227 | const style = layer.getStyle() as Style; 228 | expect(style.getFill()).toBeFalsy(); 229 | expect(style.getImage()).toBeInstanceOf(CircleStyle); 230 | const image = style.getImage() as CircleStyle; 231 | expect(image.getFill().getColor()).toBe('red'); 232 | expect(image.getStroke().getColor()).toBe('blue'); 233 | }); 234 | 235 | test('correctly attaches text to a style', async () => { 236 | const [_, map] = await miniRender( 237 | 238 | 239 | 240 | 241 | 242 | 243 | , 244 | ); 245 | const layer = map.getLayers().getArray()[0] as VectorLayer>; 246 | expect(layer.getStyle()).toBeInstanceOf(Style); 247 | const style = layer.getStyle() as Style; 248 | expect(style.getFill()).toBeFalsy(); 249 | expect(style.getText()).toBeInstanceOf(TextStyle); 250 | const text = style.getText() as TextStyle; 251 | expect(text.getFill().getColor()).toBe('red'); 252 | expect(text.getText()).toBe('abc'); 253 | }); 254 | 255 | test('correctly attaches primitives with auto attach', async () => { 256 | const [_, map] = await miniRender( 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | , 269 | ); 270 | const layer = map.getLayers().getArray()[0] as VectorLayer>; 271 | expect(layer.getSource().getFeatures()).toHaveLength(3); 272 | expect(layer.getSource().getFeatures()[0].getGeometry()).toBeInstanceOf(Point); 273 | expect(layer.getSource().getFeatures()[1].getGeometry()).toBeInstanceOf(Circle); 274 | expect(layer.getSource().getFeatures()[2].getGeometry()).toBeInstanceOf(Polygon); 275 | expect(layer.getStyle()).toBeInstanceOf(Style); 276 | }); 277 | 278 | test('correctly attaches children in primitives', async () => { 279 | const [_, map] = await miniRender( 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | , 295 | ); 296 | const layer = map.getLayers().getArray()[0] as VectorLayer>; 297 | expect(layer.getSource().getFeatures()).toHaveLength(3); 298 | expect(layer.getSource().getFeatures()[0].getGeometry()).toBeInstanceOf(Point); 299 | expect(layer.getSource().getFeatures()[1].getGeometry()).toBeInstanceOf(Circle); 300 | expect(layer.getSource().getFeatures()[2].getGeometry()).toBeInstanceOf(Polygon); 301 | expect(layer.getStyle()).toBeInstanceOf(Style); 302 | const style = layer.getStyle() as Style; 303 | expect(style.getFill().getColor()).toBe('red'); 304 | expect(style.getStroke().getColor()).toBe('blue'); 305 | }); 306 | 307 | test('correctly attaches a view', async () => { 308 | const [_, map] = await miniRender(); 309 | map.on('change:view', () => expect(map.getView().getCenter()).toBe([42, 42])); 310 | }); 311 | 312 | test('correctly attaches immutable components in order after reconstruction', async () => { 313 | let counter = 0; 314 | let externalEvent = (value: number) => {}; 315 | function OrthoImageSource({ url }: { url: string }) { 316 | return ( 317 | { 320 | if (e && e.test === undefined) { 321 | e.test = counter++; 322 | } 323 | }} 324 | /> 325 | ); 326 | } 327 | function ImageLayer({ children, opacity = 1 }: PropsWithChildren<{ opacity?: number }>) { 328 | return {children}; 329 | } 330 | function Inner() { 331 | const [state, setState] = useState(0.3); 332 | externalEvent = setState; 333 | return ( 334 | <> 335 | 336 | 337 | 338 | 339 | { 342 | if (e && e.test === undefined) { 343 | e.test = counter++; 344 | } 345 | }} 346 | /> 347 | 348 | > 349 | ); 350 | } 351 | const [_, map, act] = await miniRender(); 352 | expect((map.getLayers().getArray()[0] as ImageLayer).getSource()).toBeInstanceOf(Static); 353 | expect((map.getLayers().getArray()[1] as ImageLayer).getSource()).toBeInstanceOf(Static); 354 | expect(((map.getLayers().getArray()[0] as ImageLayer).getSource() as any).test).toBe(0); 355 | expect(((map.getLayers().getArray()[1] as ImageLayer).getSource() as any).test).toBe(1); 356 | expect((map.getLayers().getArray()[0] as ImageLayer).getSource().getUrl()).toMatch(new RegExp('http://aaa.com?')); 357 | expect((map.getLayers().getArray()[1] as ImageLayer).getSource().getUrl()).toMatch(new RegExp('http://bbb.com?')); 358 | await act(async () => externalEvent(0.9)); 359 | await act(async () => externalEvent(0.7)); 360 | await act(async () => externalEvent(0.1)); 361 | await act(async () => externalEvent(0.2)); 362 | expect(((map.getLayers().getArray()[0] as ImageLayer).getSource() as any).test).toBe(0); 363 | expect(((map.getLayers().getArray()[1] as ImageLayer).getSource() as any).test).toBe(1); 364 | expect((map.getLayers().getArray()[0] as ImageLayer).getSource().getUrl()).toMatch(new RegExp('http://aaa.com?')); 365 | expect((map.getLayers().getArray()[1] as ImageLayer).getSource().getUrl()).toMatch(new RegExp('http://bbb.com?')); 366 | }); 367 | }); 368 | 369 | describe('Dynamic attachments', () => { 370 | test('correctly attaches and removes a style from a layer', async () => { 371 | let externalEvent = (value: number) => {}; 372 | function Component() { 373 | const [state, setState] = useState(0); 374 | externalEvent = setState; 375 | return {state > 0 && {state > 1 && }}; 376 | } 377 | const [_, map, act] = await miniRender(); 378 | const getLayer = () => map.getLayers().getArray()[0] as VectorLayer>; 379 | expect(getLayer().getStyle()).toBeInstanceOf(Function); 380 | await act(async () => externalEvent(1)); 381 | expect(getLayer().getStyle()).toBeTruthy(); 382 | expect((getLayer().getStyle() as Style).getFill()).toBeFalsy(); 383 | await act(async () => externalEvent(2)); 384 | expect((getLayer().getStyle() as Style).getFill()).toBeInstanceOf(Fill); 385 | expect((getLayer().getStyle() as Style).getFill().getColor()).toBe('red'); 386 | await act(async () => externalEvent(1)); 387 | expect((getLayer().getStyle() as Style).getFill()).toBeFalsy(); 388 | await act(async () => externalEvent(0)); 389 | expect(getLayer().getStyle()).toBeFalsy(); 390 | }); 391 | 392 | test('correctly attaches features in order from state with timeout', async () => { 393 | let externalEvent = () => {}; 394 | function Component() { 395 | const [state, setState] = useState(null); 396 | externalEvent = () => 397 | setState([ 398 | [0, 0], 399 | [1, 1], 400 | [2, 2], 401 | ]); 402 | return ( 403 | 404 | {state 405 | ? state.map((c, i) => ( 406 | 407 | 408 | 409 | )) 410 | : null} 411 | 412 | ); 413 | } 414 | const [_, map, act] = await miniRender( 415 | 416 | 417 | , 418 | ); 419 | const getLayer = () => map.getLayers().getArray()[0] as VectorLayer>; 420 | expect(getLayer().getSource().getFeatures()).toHaveLength(0); 421 | await act(async () => externalEvent()); 422 | expect(getLayer().getSource().getFeatures()).toHaveLength(3); 423 | expect(getLayer().getSource().getFeatures()[0].getGeometry()).toBeInstanceOf(Point); 424 | expect((getLayer().getSource().getFeatures()[0].getGeometry() as Point).getCoordinates()).toStrictEqual([0, 0]); 425 | expect(getLayer().getSource().getFeatures()[1].getGeometry()).toBeInstanceOf(Point); 426 | expect((getLayer().getSource().getFeatures()[1].getGeometry() as Point).getCoordinates()).toStrictEqual([1, 1]); 427 | expect(getLayer().getSource().getFeatures()[2].getGeometry()).toBeInstanceOf(Point); 428 | expect((getLayer().getSource().getFeatures()[2].getGeometry() as Point).getCoordinates()).toStrictEqual([2, 2]); 429 | }); 430 | 431 | test('correctly attaches and removes a new layer in the middle after a while', async () => { 432 | let externalEvent = (value: boolean) => {}; 433 | function Component() { 434 | const [state, setState] = useState(false); 435 | externalEvent = setState; 436 | return ( 437 | <> 438 | 439 | {state && } 440 | 441 | > 442 | ); 443 | } 444 | const [_, map, act] = await miniRender(); 445 | expect(map.getLayers().getLength()).toBe(2); 446 | expect(map.getLayers().getArray()[0]).toBeInstanceOf(VectorLayer); 447 | expect(map.getLayers().getArray()[1]).toBeInstanceOf(ImageLayer); 448 | await act(async () => externalEvent(true)); 449 | expect(map.getLayers().getLength()).toBe(3); 450 | expect(map.getLayers().getArray()[0]).toBeInstanceOf(VectorLayer); 451 | expect(map.getLayers().getArray()[1]).toBeInstanceOf(TileLayer); 452 | expect(map.getLayers().getArray()[2]).toBeInstanceOf(ImageLayer); 453 | await act(async () => externalEvent(false)); 454 | expect(map.getLayers().getLength()).toBe(2); 455 | expect(map.getLayers().getArray()[0]).toBeInstanceOf(VectorLayer); 456 | expect(map.getLayers().getArray()[1]).toBeInstanceOf(ImageLayer); 457 | }); 458 | 459 | test('correctly attaches and removes a new layer in the beginning after a while', async () => { 460 | let externalEvent = (value: boolean) => {}; 461 | function Component() { 462 | const [state, setState] = useState(false); 463 | externalEvent = setState; 464 | return ( 465 | <> 466 | {state && } 467 | 468 | 469 | > 470 | ); 471 | } 472 | const [_, map, act] = await miniRender(); 473 | expect(map.getLayers().getLength()).toBe(2); 474 | expect(map.getLayers().getArray()[0]).toBeInstanceOf(VectorLayer); 475 | expect(map.getLayers().getArray()[1]).toBeInstanceOf(ImageLayer); 476 | await act(async () => externalEvent(true)); 477 | expect(map.getLayers().getLength()).toBe(3); 478 | expect(map.getLayers().getArray()[0]).toBeInstanceOf(TileLayer); 479 | expect(map.getLayers().getArray()[1]).toBeInstanceOf(VectorLayer); 480 | expect(map.getLayers().getArray()[2]).toBeInstanceOf(ImageLayer); 481 | await act(async () => externalEvent(false)); 482 | expect(map.getLayers().getLength()).toBe(2); 483 | expect(map.getLayers().getArray()[0]).toBeInstanceOf(VectorLayer); 484 | expect(map.getLayers().getArray()[1]).toBeInstanceOf(ImageLayer); 485 | }); 486 | 487 | test('correctly attaches and removes a new layer in the end after a while', async () => { 488 | let externalEvent = (value: boolean) => {}; 489 | function Component() { 490 | const [state, setState] = useState(false); 491 | externalEvent = setState; 492 | return ( 493 | <> 494 | 495 | 496 | {state && } 497 | > 498 | ); 499 | } 500 | const [_, map, act] = await miniRender(); 501 | expect(map.getLayers().getLength()).toBe(2); 502 | expect(map.getLayers().getArray()[0]).toBeInstanceOf(VectorLayer); 503 | expect(map.getLayers().getArray()[1]).toBeInstanceOf(ImageLayer); 504 | await act(async () => externalEvent(true)); 505 | expect(map.getLayers().getLength()).toBe(3); 506 | expect(map.getLayers().getArray()[0]).toBeInstanceOf(VectorLayer); 507 | expect(map.getLayers().getArray()[1]).toBeInstanceOf(ImageLayer); 508 | expect(map.getLayers().getArray()[2]).toBeInstanceOf(TileLayer); 509 | await act(async () => externalEvent(false)); 510 | expect(map.getLayers().getLength()).toBe(2); 511 | expect(map.getLayers().getArray()[0]).toBeInstanceOf(VectorLayer); 512 | expect(map.getLayers().getArray()[1]).toBeInstanceOf(ImageLayer); 513 | }); 514 | }); 515 | -------------------------------------------------------------------------------- /tests/props.test.tsx: -------------------------------------------------------------------------------- 1 | import { miniRender } from './utils'; 2 | import React, { useState } from 'react'; 3 | 4 | import { Fill, Style } from 'ol/style'; 5 | import VectorLayer from 'ol/layer/Vector'; 6 | import VectorSource, { VectorSourceEvent } from 'ol/source/Vector'; 7 | import { Point } from 'ol/geom'; 8 | 9 | describe('Args prop', () => { 10 | test('creates and recreates a style from args', async () => { 11 | let externalEvent = (value: boolean) => {}; 12 | function MyStyle() { 13 | const [state, setState] = useState(false); 14 | externalEvent = setState; 15 | return ( 16 | 17 | 18 | 19 | ); 20 | } 21 | const [_, map, act] = await miniRender( 22 | 23 | 24 | , 25 | ); 26 | const layer = map.getLayers().getArray()[0] as VectorLayer>; 27 | const style = layer.getStyle() as Style; 28 | expect(style.getFill().getColor()).toBe('blue'); 29 | await act(async () => externalEvent(true)); 30 | expect(style.getFill().getColor()).toBe('red'); 31 | await act(async () => externalEvent(false)); 32 | expect(style.getFill().getColor()).toBe('blue'); 33 | }); 34 | 35 | test('does not recreate the object when the args does not change', async () => { 36 | let externalEvent = (value: number) => {}; 37 | function MyStyle() { 38 | const [state, setState] = useState(1); 39 | externalEvent = setState; 40 | return ( 41 | 42 | 43 | 44 | 45 | ); 46 | } 47 | const [_, map, act] = await miniRender( 48 | 49 | 50 | , 51 | ); 52 | const layer = map.getLayers().getArray()[0] as VectorLayer>; 53 | const style = layer.getStyle() as Style; 54 | (style.getFill() as any).__instance = 42; 55 | expect(style.getStroke().getWidth()).toBe(1); 56 | await act(async () => externalEvent(2)); 57 | expect((style.getFill() as any).__instance).toBe(42); 58 | expect(style.getStroke().getWidth()).toBe(2); 59 | await act(async () => externalEvent(3)); 60 | expect((style.getFill() as any).__instance).toBe(42); 61 | expect(style.getStroke().getWidth()).toBe(3); 62 | }); 63 | 64 | test('updates the ref on recreation', async () => { 65 | let externalEvent = (value: boolean) => {}; 66 | let styleRef = { current: null as null | Style }; 67 | let fillRef = { current: null as null | Fill }; 68 | function MyStyle() { 69 | const [state, setState] = useState(false); 70 | externalEvent = setState; 71 | return ( 72 | 73 | 74 | 75 | ); 76 | } 77 | const [, , act] = await miniRender( 78 | 79 | 80 | , 81 | ); 82 | expect(styleRef.current?.getFill().getColor()).toBe('blue'); 83 | expect(fillRef.current?.getColor()).toBe('blue'); 84 | await act(async () => externalEvent(true)); 85 | expect(styleRef.current?.getFill().getColor()).toBe('red'); 86 | expect(fillRef.current?.getColor()).toBe('red'); 87 | await act(async () => externalEvent(false)); 88 | expect(styleRef.current?.getFill().getColor()).toBe('blue'); 89 | expect(fillRef.current?.getColor()).toBe('blue'); 90 | }); 91 | }); 92 | 93 | describe('Props', () => { 94 | test('apply props with more priority than args', async () => { 95 | let externalEvent = (value: boolean) => {}; 96 | function MyStyle() { 97 | const [state, setState] = useState(false); 98 | externalEvent = setState; 99 | return ( 100 | 101 | 102 | 103 | ); 104 | } 105 | const [_, map, act] = await miniRender( 106 | 107 | 108 | , 109 | ); 110 | const layer = map.getLayers().getArray()[0] as VectorLayer>; 111 | const style = layer.getStyle() as Style; 112 | expect(style.getFill().getColor()).toBe('blue'); 113 | await act(async () => externalEvent(true)); 114 | expect(style.getFill().getColor()).toBe('red'); 115 | await act(async () => externalEvent(false)); 116 | expect(style.getFill().getColor()).toBe('blue'); 117 | }); 118 | 119 | test('apply props after recreation', async () => { 120 | let externalEvent = (value: boolean) => {}; 121 | function MyStyle() { 122 | const [state, setState] = useState(false); 123 | externalEvent = setState; 124 | return ( 125 | 126 | 127 | 128 | ); 129 | } 130 | const [_, map, act] = await miniRender( 131 | 132 | 133 | , 134 | ); 135 | const layer = map.getLayers().getArray()[0] as VectorLayer>; 136 | const style = layer.getStyle() as Style; 137 | expect(style.getStroke().getColor()).toBe('blue'); 138 | expect(style.getStroke().getWidth()).toBe(3); 139 | await act(async () => externalEvent(true)); 140 | expect(style.getStroke().getColor()).toBe('red'); 141 | expect(style.getStroke().getWidth()).toBe(3); 142 | await act(async () => externalEvent(false)); 143 | expect(style.getStroke().getColor()).toBe('blue'); 144 | expect(style.getStroke().getWidth()).toBe(3); 145 | }); 146 | 147 | test('apply and remove event props', async () => { 148 | let extSetFeature = (value: boolean) => {}; 149 | let extSetHandler = (value: null | ((e: VectorSourceEvent) => void)) => {}; 150 | let nCalled = 0; 151 | function Component() { 152 | const [feature, setFeature] = useState(false); 153 | extSetFeature = setFeature; 154 | const [handler, setHandler] = useState(() => {}); 155 | extSetHandler = setHandler; 156 | return ( 157 | 158 | {feature && ( 159 | 160 | 161 | 162 | )} 163 | 164 | ); 165 | } 166 | const [, , act] = await miniRender( 167 | 168 | 169 | , 170 | ); 171 | await act(async () => 172 | extSetHandler(() => (e: VectorSourceEvent) => { 173 | expect(e.feature?.getGeometry()).toBeInstanceOf(Point); 174 | nCalled++; 175 | }), 176 | ); 177 | await act(async () => extSetFeature(true)); 178 | await act(async () => extSetHandler(null)); 179 | await act(async () => extSetFeature(false)); 180 | await act(async () => extSetFeature(true)); 181 | expect(nCalled).toBe(1); 182 | }); 183 | }); 184 | -------------------------------------------------------------------------------- /tests/utils.test.ts: -------------------------------------------------------------------------------- 1 | import { pascalCase, shallowCompare, pruneKeys } from '../src/utils'; 2 | 3 | describe('Utils', () => { 4 | test('turns a string into pascal case', () => { 5 | expect(pascalCase('testAbc')).toBe('TestAbc'); 6 | }); 7 | 8 | test('returns true only if two elements are the same', () => { 9 | const obj = {} as const; 10 | expect(shallowCompare(obj, obj)).toBeTruthy(); 11 | const arr = [] as const; 12 | expect(shallowCompare(arr, arr)).toBeTruthy(); 13 | expect(shallowCompare('test', 'test')).toBeTruthy(); 14 | expect(shallowCompare(false, false)).toBeTruthy(); 15 | expect(shallowCompare(null, null)).toBeTruthy(); 16 | expect(shallowCompare(42, 42)).toBeTruthy(); 17 | 18 | expect(shallowCompare('test', 'ttt')).toBeFalsy(); 19 | expect(shallowCompare(false, true)).toBeFalsy(); 20 | expect(shallowCompare(null, 3)).toBeFalsy(); 21 | expect(shallowCompare(42, 52)).toBeFalsy(); 22 | }); 23 | 24 | test('returns true only if two elements have the same first-level children', () => { 25 | const childPtr = [] as const; 26 | const childPtr2 = [] as const; 27 | 28 | const objA = { testBoolean: true, testNumber: 42, testString: 'abc', testPtr: childPtr }; 29 | const objB = { testBoolean: true, testNumber: 42, testString: 'abc', testPtr: childPtr }; 30 | expect(shallowCompare(objA, objB)).toBeTruthy(); 31 | const arrA = [true, 42, 'abc', childPtr] as const; 32 | const arrB = [true, 42, 'abc', childPtr] as const; 33 | expect(shallowCompare(arrA, arrB)).toBeTruthy(); 34 | 35 | const objA2 = { testBoolean: true, testNumber: 42, testString: 'abc', testPtr: childPtr }; 36 | const objB2 = { testBoolean: true, testNumber: 42, testString: 'abc', testPtr: childPtr2 }; 37 | expect(shallowCompare(objA2, objB2)).toBeFalsy(); 38 | const arrA2 = [true, 42, 'abc', childPtr] as const; 39 | const arrB2 = [true, 42, 'abc', childPtr2] as const; 40 | expect(shallowCompare(arrA2, arrB2)).toBeFalsy(); 41 | 42 | const objA3 = { testBoolean: true, testNumber: 42, testString: 'abc', testPtr: childPtr }; 43 | const objB3 = { testBoolean: true, testNumber: 42, testPtr: childPtr2 }; 44 | expect(shallowCompare(objA3, objB3)).toBeFalsy(); 45 | const arrA3 = ['abc', childPtr] as const; 46 | const arrB3 = [true, 42, 'abc', childPtr] as const; 47 | expect(shallowCompare(arrA3, arrB3)).toBeFalsy(); 48 | }); 49 | 50 | test('correctly prunes the required keys', () => { 51 | expect(pruneKeys({ abc: 42, def: 'test' }, ['abc']).abc).toBeUndefined(); 52 | expect(pruneKeys({ abc: 42, def: 'test' }, ['abc', 'def'])).toEqual({}); 53 | expect(pruneKeys({ abc: 42, def: 'test' }, ['eee'])).toEqual({ abc: 42, def: 'test' }); 54 | }); 55 | }); 56 | -------------------------------------------------------------------------------- /tests/utils.tsx: -------------------------------------------------------------------------------- 1 | (global as any).URL.createObjectURL = function () {}; 2 | jest.mock('scheduler', () => require('scheduler/unstable_mock')); 3 | 4 | import { Map, View } from 'ol'; 5 | 6 | import { reconciler } from '../src/reconciler'; 7 | import '../src/index'; 8 | 9 | export async function miniRender(element: JSX.Element) { 10 | const rootDiv = document.createElement('div'); 11 | const map = new Map({ 12 | target: rootDiv, 13 | layers: [], 14 | interactions: [], 15 | controls: [], 16 | view: new View(), 17 | }); 18 | 19 | await reconciler.act(async () => { 20 | const root = reconciler.createContainer(map as any, 1, false, null); 21 | reconciler.updateContainer(element, root, null, () => undefined); 22 | }); 23 | 24 | return [rootDiv, map, reconciler.act] as const; 25 | } 26 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "esModuleInterop": true, 7 | "jsx": "react", 8 | "pretty": true, 9 | "strict": true, 10 | "skipLibCheck": true, 11 | "declaration": true, 12 | "declarationDir": "dist", 13 | "removeComments": true, 14 | "emitDeclarationOnly": true, 15 | "resolveJsonModule": true, 16 | "noImplicitThis": false, 17 | "baseUrl": "./", 18 | "allowJs": true 19 | }, 20 | "include": ["src/**/*"] 21 | } 22 | --------------------------------------------------------------------------------