├── .github └── workflows │ └── deploy.yml ├── .gitignore ├── .prettierrc.js ├── .storybook ├── AZMtheme.js ├── main.ts ├── manager.js └── preview.tsx ├── README.md ├── SECURITY.md ├── desktop.ini ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo1.png ├── logo192.png ├── logo2.png ├── logo512.png └── manifest.json ├── src ├── Storybook.css ├── key.ts └── stories │ ├── BasicUsage │ ├── MapControls │ │ ├── MapControl.mdx │ │ └── MapControl.tsx │ ├── MapRef │ │ ├── MapRef.mdx │ │ ├── SetCenter.tsx │ │ └── SetStyle.tsx │ └── MapStyles │ │ ├── MapStyles.mdx │ │ ├── MapStyles.stories.ts │ │ └── MapStyles.tsx │ ├── DataVisualization │ ├── BubbleLayer │ │ ├── BubbleLayer.mdx │ │ ├── BubbleLayer.stories.ts │ │ └── BubbleLayer.tsx │ ├── ClusterAggregates │ │ ├── ClusterAggregate.mdx │ │ └── ClusterAggregates.tsx │ ├── Datavisualization.mdx │ ├── HeatMapLayer │ │ ├── HeatMapLayer.mdx │ │ ├── HeatMapLayer.stories.ts │ │ └── HeatMapLayer.tsx │ ├── ImageLayer │ │ ├── ImageLayer.mdx │ │ ├── ImageLayer.stories.ts │ │ └── ImageLayer.tsx │ ├── LineLayer │ │ ├── LineLayer.mdx │ │ ├── LineLayer.stories.ts │ │ └── LineLayer.tsx │ ├── PolygonExtrusion │ │ ├── PolygonExtrusion.mdx │ │ ├── PolygonExtrusion.stories.ts │ │ └── PolygonExtrusion.tsx │ ├── PolygonLayer │ │ ├── PolygonLayer.mdx │ │ ├── PolygonLayer.stories.ts │ │ └── PolygonLayer.tsx │ ├── SymbolLayer │ │ ├── SymbolLayer.mdx │ │ ├── SymbolLayer.stories.tsx │ │ └── SymbolLayer.tsx │ └── TileLayer │ │ ├── TileLayer.mdx │ │ ├── TileLayer.stories.ts │ │ └── TileLayer.tsx │ ├── DefaultMap │ ├── DefaultMap.tsx │ └── GettingStarted.mdx │ ├── Events │ ├── HtmlMarkerEvents │ │ ├── HtmlMarkerEvents.css │ │ ├── HtmlMarkerEvents.mdx │ │ └── HtmlMarkerEvents.tsx │ ├── LayerEvents │ │ ├── LayerEvents.mdx │ │ └── LayerEvents.tsx │ └── MapEvents │ │ ├── MapEvents.mdx │ │ └── MapEvents.tsx │ └── MapAnnotations │ ├── HtmlMarker │ ├── HtmlMarker.mdx │ ├── HtmlMarker.stories.tsx │ └── HtmlMarker.tsx │ └── Popup │ ├── Accessible │ ├── AccessiblePopup.mdx │ └── AccessiblePopup.tsx │ ├── Basic │ ├── Popup.mdx │ ├── Popup.stories.ts │ └── Popup.tsx │ └── Interactive │ ├── InteractivePopup.mdx │ ├── InteractivePopup.stories.ts │ ├── InteractivePopup.tsx │ ├── InteractivePopupExample.tsx │ └── PopupContent.tsx └── tsconfig.json /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy Storybook to GitHub Pages 2 | 3 | on: 4 | push: 5 | branches: 6 | - storybook 7 | 8 | permissions: 9 | contents: write 10 | pages: write 11 | 12 | jobs: 13 | build-and-deploy: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - name: Checkout code 18 | uses: actions/checkout@v4 19 | 20 | - name: Setup Node.js 21 | uses: actions/setup-node@v4 22 | with: 23 | node-version: '20.16.0' 24 | 25 | - name: Install dependencies 26 | run: npm install 27 | 28 | - name: Build Storybook 29 | env: 30 | STORYBOOK_AZURE_MAPS_KEY: ${{ secrets.STORYBOOK_AZURE_MAPS_KEY }} 31 | run: npm run build-storybook 32 | 33 | - name: Deploy to GitHub Pages 34 | uses: peaceiris/actions-gh-pages@v3 35 | with: 36 | github_token: ${{ secrets.GITHUB_TOKEN }} 37 | publish_dir: ./storybook-static 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.yalc 6 | /.pnp 7 | .pnp.js 8 | 9 | # testing 10 | /coverage 11 | 12 | # production 13 | /build 14 | 15 | # misc 16 | .DS_Store 17 | .env 18 | .env.local 19 | .env.development.local 20 | .env.test.local 21 | .env.production.local 22 | 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | *storybook.log -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | semi: true, 3 | trailingComma: 'all', 4 | singleQuote: true, 5 | printWidth: 120, 6 | tabWidth: 2, 7 | endOfLine: 'auto', 8 | }; 9 | -------------------------------------------------------------------------------- /.storybook/AZMtheme.js: -------------------------------------------------------------------------------- 1 | import { create } from '@storybook/theming/create'; 2 | 3 | export default create({ 4 | base: 'light', 5 | brandTitle: 'React Azure Maps', 6 | brandUrl: 'https://github.com/Azure/react-azure-maps', 7 | brandImage: 'logo1.png', 8 | brandTarget: '_blank', 9 | }); 10 | -------------------------------------------------------------------------------- /.storybook/main.ts: -------------------------------------------------------------------------------- 1 | import type { StorybookConfig } from '@storybook/react-webpack5'; 2 | 3 | const config: StorybookConfig = { 4 | stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], 5 | addons: [ 6 | '@storybook/preset-create-react-app', 7 | '@storybook/addon-onboarding', 8 | '@storybook/addon-links', 9 | { 10 | name: '@storybook/addon-essentials', 11 | options: { 12 | actions: false, 13 | interactions: false, 14 | }, 15 | }, 16 | { 17 | name: '@storybook/addon-storysource', 18 | options: { 19 | loaderOptions: { 20 | parser: 'typescript', 21 | }, 22 | }, 23 | }, 24 | ], 25 | framework: { 26 | name: '@storybook/react-webpack5', 27 | options: {}, 28 | }, 29 | staticDirs: ['../public'], 30 | }; 31 | export default config; 32 | -------------------------------------------------------------------------------- /.storybook/manager.js: -------------------------------------------------------------------------------- 1 | import { addons } from '@storybook/manager-api'; 2 | import Theme from './AZMtheme'; 3 | 4 | addons.setConfig({ 5 | theme: Theme, 6 | }); 7 | -------------------------------------------------------------------------------- /.storybook/preview.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import type { Preview } from '@storybook/react'; 3 | import 'azure-maps-control/dist/atlas.min.css'; 4 | import '../src/Storybook.css'; 5 | import { Story, Canvas } from '@storybook/addon-docs'; 6 | 7 | const preview: Preview = { 8 | parameters: { 9 | layout: 'centered', 10 | controls: { 11 | matchers: { 12 | color: /(background|color)$/i, 13 | date: /Date$/i, 14 | }, 15 | }, 16 | options: { 17 | storySort: { 18 | order: [ 19 | 'Getting Started', 20 | 'Basic Usage', 21 | ['*', 'Map Reference'], 22 | 'Map Annotations', 23 | '*', 24 | 'Data Visualization', 25 | ['Introduction'], 26 | 'Events', 27 | ['Map Events'], 28 | ], 29 | }, 30 | }, 31 | }, 32 | }; 33 | 34 | export default preview; 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Azure Maps Playground 2 | 3 | The **React Azure Map Playground** utilize [Storybook](https://storybook.js.org/) to provide an interactive way to explore and experiment with the [**react-azure-maps**](https://github.com/Azure/react-azure-maps) components.
4 | Visit React Azure Map Playground: https://azure.github.io/react-azure-maps-playground/ 5 | 6 | ## Usage 7 | 8 | In the playground, you can: 9 | 10 | - Learn how to effectively integrate Azure Maps into your React projects 11 | - Adjust the component settings via the controls provided. 12 | - Observe the immediate effects of your changes on the map. 13 | - Read the full source code of the examples. 14 | 15 | ## Running Locally 16 | 17 | Before you begin, ensure you have met the following requirements: 18 | 19 | - **Node.js**: Make sure you have Node.js installed on your machine. This project uses Node.js v20.16.0. 20 | - **npm**: npm is required for package management. It comes with Node.js, so you should have it by default. 21 | 22 | ### Installation 23 | 24 | Follow these steps to get your local environment up and running: 25 | 26 | 1. **Clone the Repository** 27 | 28 | ```bash 29 | git clone https://github.com/Azure/react-azure-maps-playground.git 30 | ``` 31 | 32 | 2. **Navigate to the Project Directory** 33 | 34 | ```bash 35 | cd react-azure-maps-playground 36 | ``` 37 | 38 | 3. **Install Dependencies** 39 | 40 | Use npm to install the necessary packages: 41 | 42 | ```bash 43 | npm install 44 | ``` 45 | 46 | ### Running the Project 47 | 48 | Once the dependencies are installed, you can run the project locally.
49 | Start the Storybook server to view and interact with the components: 50 | 51 | ```bash 52 | npm run storybook 53 | ``` 54 | 55 | Open your web browser and navigate to [http://localhost:6006](http://localhost:6006) to view the Storybook interface. 56 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /desktop.ini: -------------------------------------------------------------------------------- 1 | [.ShellClassInfo] 2 | IconResource=C:\Windows\System32\SHELL32.dll,41 3 | [ViewState] 4 | Mode= 5 | Vid= 6 | FolderType=Generic 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "azure-maps-storybook", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "azure-maps-control": "^3.3.0", 7 | "react": "^18.2.0", 8 | "react-azure-maps": "^1.0.3", 9 | "react-dom": "^18.2.0", 10 | "react-router-dom": "^6.3.0" 11 | }, 12 | "scripts": { 13 | "storybook": "storybook dev -p 6006", 14 | "build-storybook": "storybook build" 15 | }, 16 | "eslintConfig": { 17 | "extends": [ 18 | "react-app", 19 | "plugin:storybook/recommended" 20 | ] 21 | }, 22 | "browserslist": { 23 | "production": [ 24 | ">0.2%", 25 | "not dead", 26 | "not op_mini all" 27 | ], 28 | "development": [ 29 | "last 1 chrome version", 30 | "last 1 firefox version", 31 | "last 1 safari version" 32 | ] 33 | }, 34 | "devDependencies": { 35 | "@babel/plugin-proposal-private-property-in-object": "^7.21.11", 36 | "@storybook/addon-essentials": "^8.2.4", 37 | "@storybook/addon-interactions": "^8.2.4", 38 | "@storybook/addon-links": "^8.2.4", 39 | "@storybook/addon-onboarding": "^8.2.4", 40 | "@storybook/addon-storysource": "^8.2.4", 41 | "@storybook/blocks": "^8.2.4", 42 | "@storybook/preset-create-react-app": "^8.2.4", 43 | "@storybook/react": "^8.2.4", 44 | "@storybook/react-webpack5": "^8.2.4", 45 | "@storybook/test": "^8.2.4", 46 | "@types/node": "12.11.7", 47 | "@types/react": "^18.0.15", 48 | "@types/react-dom": "^18.0.6", 49 | "@types/react-router-dom": "^5.1.3", 50 | "eslint-plugin-storybook": "^0.8.0", 51 | "prettier": "^2.1.2", 52 | "prop-types": "15.8.1", 53 | "react-scripts": "5.0.1", 54 | "storybook": "^8.2.4", 55 | "typescript": "^4.9.5", 56 | "webpack": "5.93.0" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/react-azure-maps-playground/68b59ed119628cd85a3d65b8e55d9ca17a4f9efc/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React Azure Maps Playground 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /public/logo1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/react-azure-maps-playground/68b59ed119628cd85a3d65b8e55d9ca17a4f9efc/public/logo1.png -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/react-azure-maps-playground/68b59ed119628cd85a3d65b8e55d9ca17a4f9efc/public/logo192.png -------------------------------------------------------------------------------- /public/logo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/react-azure-maps-playground/68b59ed119628cd85a3d65b8e55d9ca17a4f9efc/public/logo2.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/react-azure-maps-playground/68b59ed119628cd85a3d65b8e55d9ca17a4f9efc/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /src/Storybook.css: -------------------------------------------------------------------------------- 1 | .defaultMap { 2 | width: 700px; 3 | height: 300px; 4 | } 5 | @media screen and (max-width: 700px) { 6 | .defaultMap { 7 | width: 90vw; 8 | height: 200px; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/key.ts: -------------------------------------------------------------------------------- 1 | import { IAzureMapOptions, AuthenticationType } from 'react-azure-maps'; 2 | 3 | export const mapOptions: IAzureMapOptions = { 4 | authOptions: { 5 | authType: AuthenticationType.anonymous, 6 | clientId: '2a60774c-f588-423b-b004-56d213773ee6', 7 | getToken: (resolve, reject) => { 8 | fetch('https://anonymous-auth.azurewebsites.net/api/GetAccessToken-Prod') 9 | .then((result) => result.text()) 10 | .then((result) => resolve(result)) 11 | .catch((error) => reject(new Error(`Failed to fetch anon auth token: ${error.message}`))); 12 | }, 13 | }, 14 | center: [0, 0], 15 | view: 'Auto', 16 | }; 17 | -------------------------------------------------------------------------------- /src/stories/BasicUsage/MapControls/MapControl.mdx: -------------------------------------------------------------------------------- 1 | import { Source, Meta } from '@storybook/addon-docs/blocks'; 2 | 3 | import MapControl, { controls } from './MapControl'; 4 | 5 | 6 | 7 | # Map Control 8 | These examples show all the map navigation controls on the map and how to customize different option settings. 9 | 10 | ## Style Control 11 | 12 | 13 | 14 | 22 | 32 | ; 33 | `}/> 34 | 35 | ## Zoom Control 36 | 37 | 38 | 46 | 55 | ; 56 | `}/> 57 | 58 | ## Compass Control 59 | 60 | 61 | 69 | 79 | ; 80 | `}/> 81 | 82 | ## Pitch Control 83 | 84 | 85 | 93 | 103 | ; 104 | `}/> 105 | 106 | ## Traffic Control and Legend Control 107 | 108 | 109 | 117 | 132 | ; 133 | `}/> 134 | 135 | ## Scale Control 136 | 137 | 138 | 146 | 155 | ; 156 | `}/> 157 | 158 | ## Fullscreen Control 159 | 160 | 161 | 169 | 178 | ; 179 | `}/> -------------------------------------------------------------------------------- /src/stories/BasicUsage/MapControls/MapControl.tsx: -------------------------------------------------------------------------------- 1 | import { IAzureMapControls, ControlOptions } from 'react-azure-maps'; 2 | import DefaultMap from '../../DefaultMap/DefaultMap'; 3 | import { mapOptions } from '../../../key'; 4 | 5 | export interface ControlProps { 6 | controls: IAzureMapControls[]; 7 | } 8 | 9 | export const controls: IAzureMapControls[] = [ 10 | { 11 | controlName: 'StyleControl', 12 | controlOptions: { mapStyles: 'all' }, 13 | options: { position: 'top-right' } as ControlOptions, 14 | }, 15 | { 16 | controlName: 'ZoomControl', 17 | options: { position: 'top-right' } as ControlOptions, 18 | }, 19 | { 20 | controlName: 'CompassControl', 21 | controlOptions: { rotationDegreesDelta: 10, style: 'dark' }, 22 | options: { position: 'bottom-right' } as ControlOptions, 23 | }, 24 | { 25 | controlName: 'PitchControl', 26 | controlOptions: { pitchDegreesDelta: 5, style: 'dark' }, 27 | options: { position: 'bottom-right' } as ControlOptions, 28 | }, 29 | { 30 | controlName: 'TrafficControl', 31 | controlOptions: { incidents: true }, 32 | options: { position: 'top-left' } as ControlOptions, 33 | }, 34 | { 35 | controlName: 'TrafficLegendControl', 36 | controlOptions: {}, 37 | options: { position: 'bottom-left' } as ControlOptions, 38 | }, 39 | { 40 | controlName: 'ScaleControl', 41 | controlOptions: {}, 42 | options: { position: 'bottom-left' } as ControlOptions, 43 | }, 44 | { 45 | controlName: 'FullscreenControl', 46 | controlOptions: {}, 47 | options: { position: 'top-right' } as ControlOptions, 48 | } 49 | ]; 50 | 51 | const MapControl = ({ controls }: ControlProps) => { 52 | return ; 53 | }; 54 | 55 | export default MapControl; 56 | -------------------------------------------------------------------------------- /src/stories/BasicUsage/MapRef/MapRef.mdx: -------------------------------------------------------------------------------- 1 | import { Meta, Source } from '@storybook/blocks'; 2 | import { AzureMapsProvider } from 'react-azure-maps'; 3 | import SetStyle from './SetStyle'; 4 | import SetCenter from './SetCenter'; 5 | 6 | 7 | 8 | # Map Reference 9 | After the map object has been rendered, you can still make adjustments to the map using the `mapRef`. 10 | When accessing the map object, check `isMapReady` to make sure the map is fully loaded:
11 | 12 | { 17 | const { mapRef, isMapReady } = useAzureMaps(); 18 | 19 | useEffect(() => { 20 | if (isMapReady && mapRef) 21 | // Do something with mapRef 22 | }, [isMapReady]); 23 | 24 | return ( 25 | 26 | ); 27 | } 28 | `}/> 29 | 30 | Remember to wrap your component with AzureMapsProvider to provide the necessary context. 31 | 32 | { 37 | return( 38 | 39 | 40 | 41 | ); 42 | } 43 | `}/> 44 | 45 | ## Examples 46 | Here are two examples of how to use the `mapRef` to change the map's display.
47 | Only codes referring to the usage of `mapRef` are shown here. Don't forget to wrap your component with `AzureMapsProvider` when implementing the examples! 48 | 49 | In the first example, we use the `setCamera()` function to change the center of the map.
50 | Click the button to see the effect. 51 | 52 | 53 | 54 | 55 | 56 | { 61 | const [mapCenter, setMapCenter] = useState([0, 0] as Number[]); 62 | const { mapRef, isMapReady } = useAzureMaps(); 63 | 64 | // Set the map center when the map is ready or when the map center changes. 65 | useEffect(() => { 66 | if (isMapReady && mapRef) 67 | // Set map center 68 | mapRef.setCamera({ center: mapCenter }); 69 | }, [isMapReady, mapCenter]); 70 | 71 | return ( 72 | <> 73 | 78 |
79 | 80 |
81 | 82 | ); 83 | }; 84 | 85 | function getRandomPosition() { 86 | const randomLongitude = Math.floor(Math.random() * (180 - -180) + -180); 87 | const randomLatitude = Math.floor(Math.random() * (-90 - 90) + 90); 88 | return [randomLatitude, randomLongitude]; 89 | }; 90 | `}/> 91 | 92 | In the second example, we use the `setStyle()` function to change the map's display style. 93 | Click the button to decide whether to show tile boundaries. 94 | 95 | 96 | 97 | 98 | 99 | { 104 | const [showTileBoundaries, setShowTileBoundaries] = useState(false); 105 | const { mapRef, isMapReady } = useAzureMaps(); 106 | 107 | //Set the tile boundaries when the map is ready or when the showTileBoundaries state changes 108 | useEffect(() => { 109 | if (isMapReady && mapRef) 110 | // Toggle tile boundaries 111 | mapRef.setStyle({ showTileBoundaries }); 112 | }, [isMapReady, showTileBoundaries]); 113 | 114 | return ( 115 | <> 116 | 122 |
123 | 124 |
125 | 126 | ); 127 | }; 128 | `}/> 129 | -------------------------------------------------------------------------------- /src/stories/BasicUsage/MapRef/SetCenter.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | import { AzureMap, useAzureMaps } from 'react-azure-maps'; 3 | import { mapOptions } from '../../../key'; 4 | 5 | const SetCenter = () => { 6 | const [mapCenter, setMapCenter] = useState([0, 0] as Number[]); 7 | const { mapRef, isMapReady } = useAzureMaps(); 8 | 9 | // Set the map center when the map is ready or when the map center changes. 10 | useEffect(() => { 11 | if (isMapReady && mapRef) 12 | // set map center 13 | mapRef.setCamera({ center: mapCenter }); 14 | }, [isMapReady, mapCenter]); 15 | 16 | return ( 17 | <> 18 | 21 |
22 | 23 |
24 | 25 | ); 26 | }; 27 | 28 | const getRandomPosition = () => { 29 | const randomLongitude = Math.floor(Math.random() * (180 - -180) + -180); 30 | const randomLatitude = Math.floor(Math.random() * (-90 - 90) + 90); 31 | return [randomLatitude, randomLongitude]; 32 | }; 33 | 34 | export default SetCenter; 35 | -------------------------------------------------------------------------------- /src/stories/BasicUsage/MapRef/SetStyle.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | import { AzureMap, useAzureMaps } from 'react-azure-maps'; 3 | import { mapOptions } from '../../../key'; 4 | 5 | export interface setStyleProps { 6 | // show tile boundaries 7 | showTileBoundaries?: boolean; 8 | } 9 | 10 | const SetStyle = () => { 11 | const { mapRef, isMapReady } = useAzureMaps(); 12 | const [showTileBoundaries, setShowTileBoundaries] = useState(false); 13 | 14 | useEffect(() => { 15 | if (isMapReady && mapRef) 16 | // toggle tile boundaries 17 | mapRef.setStyle({ showTileBoundaries }); 18 | }, [isMapReady, showTileBoundaries]); 19 | 20 | return ( 21 | <> 22 | 28 |
29 | 30 |
31 | 32 | ); 33 | }; 34 | 35 | export default SetStyle; 36 | -------------------------------------------------------------------------------- /src/stories/BasicUsage/MapStyles/MapStyles.mdx: -------------------------------------------------------------------------------- 1 | import { Source, Meta } from '@storybook/blocks'; 2 | 3 | 4 | 5 | # Map Styles 6 | 7 | You can change the style of the map by passing the `styleOptions` prop like this:
8 | 9 | { 13 | return ( 14 | 15 | 24 | 25 | ); 26 | }; 27 | `}/> 28 | 29 | The props shown in the example above are all set to `true` by default.
30 | For more available properties, see the documentation for [StyleOptions](https://learn.microsoft.com/en-us/javascript/api/azure-maps-control/atlas.styleoptions?view=azure-maps-typescript-latest). 31 | -------------------------------------------------------------------------------- /src/stories/BasicUsage/MapStyles/MapStyles.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react'; 2 | import MapStyles from './MapStyles'; 3 | 4 | const meta: Meta = { 5 | title: 'Basic Usage/Map Styles', 6 | component: MapStyles, 7 | parameters: { 8 | storySource: { 9 | source: ` 10 | import { AzureMap, AzureMapsProvider } from 'react-azure-maps'; 11 | 12 | const MapStyles = () => { 13 | 14 | return ( 15 | 16 | 24 | 25 | ); 26 | }; 27 | `, 28 | }, 29 | }, 30 | }; 31 | export default meta; 32 | 33 | type Story = StoryObj; 34 | export const Example: Story = { 35 | args: { 36 | showLabels: true, 37 | showLogo: true, 38 | renderWorldCopies: true, 39 | showFeedbackLink: true, 40 | }, 41 | }; 42 | -------------------------------------------------------------------------------- /src/stories/BasicUsage/MapStyles/MapStyles.tsx: -------------------------------------------------------------------------------- 1 | import { AzureMap, AzureMapsProvider } from 'react-azure-maps'; 2 | import { mapOptions } from '../../../key'; 3 | 4 | export interface MapStylesProps { 5 | showLogo?: boolean; 6 | showLabels?: boolean; 7 | renderWorldCopies?: boolean; 8 | showFeedbackLink?: boolean; 9 | } 10 | 11 | const MapStyles = ({ showLabels, showLogo, renderWorldCopies, showFeedbackLink }: MapStylesProps) => { 12 | return ( 13 |
14 | 15 | 24 | 25 |
26 | ); 27 | }; 28 | 29 | export default MapStyles; 30 | -------------------------------------------------------------------------------- /src/stories/DataVisualization/BubbleLayer/BubbleLayer.mdx: -------------------------------------------------------------------------------- 1 | import { Meta, Source } from '@storybook/blocks'; 2 | 3 | import * as BubbleLayerStories from './BubbleLayer.stories'; 4 | 5 | import BubbleLayer from './BubbleLayer'; 6 | 7 | 8 | 9 | # Bubble Layer 10 | 11 | Bubble layers render points as circles on the map with a fixed pixel radius.
12 | You can customize the appearance of the bubbles by passing the `options` prop.
13 | For more available properties, see the documentation [BubbleLayerOptions](https://learn.microsoft.com/en-us/javascript/api/azure-maps-control/atlas.bubblelayeroptions?view=azure-maps-typescript-latest). 14 | 15 | 16 | 17 | 30 | `} /> 31 | -------------------------------------------------------------------------------- /src/stories/DataVisualization/BubbleLayer/BubbleLayer.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react'; 2 | import BubbleLayer from './BubbleLayer'; 3 | 4 | const meta: Meta = { 5 | title: 'Data Visualization/Bubble Layer', 6 | component: BubbleLayer, 7 | parameters: { 8 | storySource: { 9 | source: `import { AzureMap, AzureMapsProvider, AzureMapDataSourceProvider, AzureMapLayerProvider } from 'react-azure-maps'; 10 | import atlas, { BubbleLayerOptions } from 'azure-maps-control'; 11 | 12 | // Generate random points to build the data source for the BubbleLayer. 13 | const collection = generateRandomPoints(); 14 | 15 | const BubbleLayer = () => { 16 | 17 | 18 | 19 | 31 | 32 | 33 | 34 | ); 35 | }; 36 | 37 | function generateRandomPoints() { 38 | var layerData = []; 39 | 40 | for (var i = 0; i < 50; i++) { 41 | layerData.push( 42 | new atlas.data.Feature(new atlas.data.Point([Math.random() * 360 - 180, Math.random() * 170 - 85]), { 43 | title: 'Pin_' + i, 44 | }), 45 | ); 46 | } 47 | 48 | return layerData; 49 | } 50 | 51 | `, 52 | }, 53 | }, 54 | args: { 55 | radius: 10, 56 | color: 'DodgerBlue', 57 | opacity: 1, 58 | strokeColor: 'DarkBlue', 59 | strokeWidth: 2, 60 | strokeOpacity: 1, 61 | blur: 0, 62 | }, 63 | argTypes: { 64 | opacity: { control: { type: 'range', min: 0, max: 1, step: 0.1 } }, 65 | strokeWidth: { control: { type: 'range', min: 0, max: 10, step: 1 } }, 66 | strokeOpacity: { control: { type: 'range', min: 0, max: 1, step: 0.1 } }, 67 | blur: { control: { type: 'range', min: 0, max: 1, step: 0.1 } }, 68 | }, 69 | }; 70 | export default meta; 71 | 72 | type Story = StoryObj; 73 | 74 | export const Example: Story = {}; 75 | -------------------------------------------------------------------------------- /src/stories/DataVisualization/BubbleLayer/BubbleLayer.tsx: -------------------------------------------------------------------------------- 1 | import { mapOptions } from '../../../key'; 2 | import { AzureMap, AzureMapsProvider, AzureMapDataSourceProvider, AzureMapLayerProvider } from 'react-azure-maps'; 3 | import { BubbleLayerOptions } from 'azure-maps-control'; 4 | import atlas from 'azure-maps-control'; 5 | 6 | const collection = generateRandomPoints(); 7 | 8 | const BubbleLayer = ({ radius, color, opacity, strokeColor, strokeWidth, strokeOpacity, blur }: BubbleLayerOptions) => { 9 | return ( 10 | 11 |
12 | 13 | 14 | 26 | 27 | 28 |
29 |
30 | ); 31 | }; 32 | 33 | function generateRandomPoints() { 34 | var layerData = []; 35 | 36 | for (var i = 0; i < 50; i++) { 37 | layerData.push( 38 | new atlas.data.Feature(new atlas.data.Point([Math.random() * 360 - 180, Math.random() * 170 - 85]), { 39 | title: 'Pin_' + i, 40 | }), 41 | ); 42 | } 43 | 44 | return layerData; 45 | } 46 | 47 | export default BubbleLayer; 48 | -------------------------------------------------------------------------------- /src/stories/DataVisualization/ClusterAggregates/ClusterAggregate.mdx: -------------------------------------------------------------------------------- 1 | import { Source, Meta } from '@storybook/addon-docs/blocks'; 2 | 3 | import ClusterAggregates from './ClusterAggregates'; 4 | 5 | 6 | 7 | # Cluster Aggregates 8 | This example shows how to enable point-based clustering on a data source and render them differently from individual points on the map. 9 | Clustered points have four properties: 10 | - `cluster` - A boolean value indicating that it is a cluster. 11 | - `cluster_id` - A unique ID for the cluster which can be used with the DataSource getClusterExpansionZoom, getClusterChildren, and getClusterLeaves functions. 12 | - `point_count` - The number of points the cluster contains. 13 | - `point_count_abbreviated` - A string that abbreviates the point_count value if it is long. (i.e. 4,000 becomes 4K) 14 | 15 | For more information on clustering, see the tutorial [here](https://learn.microsoft.com/en-us/azure/azure-maps/clustering-point-data-web-sdk). 16 | 17 | You can observe the clusters by zooming in and out on the map. 18 | 19 | 20 | 21 | In this example, we use a **Bubble Layer** to render the clusters as circles and a **Symbol Layer** to render the number of points in each cluster. 22 | For the bubble layer, we set the radius and the color to change based on the numbers of points in the clusters. 23 | 24 | = 100, radius is 30 pixels. 33 | 750, 34 | 40, //If point_count >= 750, radius is 40 pixels. 35 | ], 36 | 37 | //Change the color of the cluster based on the value of the point_cluster property of the cluster. 38 | color: [ 39 | 'step', 40 | ['get', 'point_count'], 41 | 'rgba(0,255,0,0.8)', //Default to green. 42 | 100, 43 | 'rgba(255,255,0,0.8)', //If the point_count >= 100, color is yellow. 44 | 750, 45 | 'rgba(255,0,0,0.8)', //If the point_count >= 100, color is red. 46 | ], 47 | strokeWidth: 0, 48 | filter: ['has', 'point_count'], //Only render data points which have a point_count property, which clusters do. 49 | }; 50 | `}/> 51 | 52 | For the symbol layer, we set the text to be the `point_count_abbreviated` property of the cluster. 53 | 54 | 65 | 66 | Finally, we set the options of the **AzureMapDataSourceProvider** to enable clustering. 67 | 68 | { 77 | 78 | return ( 79 | 80 | 81 | 94 | 99 | 104 | 105 | 106 | 107 | ); 108 | }; 109 | `} /> -------------------------------------------------------------------------------- /src/stories/DataVisualization/ClusterAggregates/ClusterAggregates.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | AzureMap, 3 | AzureMapDataSourceProvider, 4 | AzureMapLayerProvider, 5 | AzureMapsProvider, 6 | IAzureMapOptions, 7 | } from 'react-azure-maps'; 8 | import { mapOptions } from '../../../key'; 9 | 10 | export interface ClusterAggregatesProps { 11 | showBubbles: boolean; 12 | showNumbers: boolean; 13 | } 14 | 15 | const option: IAzureMapOptions = { 16 | ...mapOptions, 17 | center: [-97, 39], 18 | zoom: 1.5, 19 | view: 'Auto', 20 | }; 21 | 22 | const bubbleLayerOptions = { 23 | //Scale the size of the clustered bubble based on the number of points inthe cluster. 24 | radius: [ 25 | 'step', 26 | ['get', 'point_count'], 27 | 20, //Default of 20 pixel radius. 28 | 100, 29 | 30, //If point_count >= 100, radius is 30 pixels. 30 | 750, 31 | 40, //If point_count >= 750, radius is 40 pixels. 32 | ], 33 | 34 | //Change the color of the cluster based on the value on the point_cluster property of the cluster. 35 | color: [ 36 | 'step', 37 | ['get', 'point_count'], 38 | 'rgba(0,255,0,0.8)', //Default to green. 39 | 100, 40 | 'rgba(255,255,0,0.8)', //If the point_count >= 100, color is yellow. 41 | 750, 42 | 'rgba(255,0,0,0.8)', //If the point_count >= 100, color is red. 43 | ], 44 | strokeWidth: 0, 45 | filter: ['has', 'point_count'], //Only rendered data points which have a point_count property, which clusters do. 46 | }; 47 | 48 | const symbolLayerOptions = { 49 | iconOptions: { 50 | image: 'none', //Hide the icon image. 51 | }, 52 | textOptions: { 53 | textField: ['get', 'point_count_abbreviated'], 54 | offset: [0, 0.4], 55 | }, 56 | }; 57 | 58 | const bubbleLayer = ( 59 | 64 | ); 65 | 66 | const symbolLayer = ( 67 | 72 | ); 73 | 74 | const ClusterAggregates = ({ showBubbles, showNumbers }: ClusterAggregatesProps) => { 75 | return ( 76 | 77 |
78 | 79 | 94 | {showBubbles ? bubbleLayer : <>} 95 | {showNumbers ? symbolLayer : <>} 96 | 97 | 98 |
99 |
100 | ); 101 | }; 102 | 103 | export default ClusterAggregates; 104 | -------------------------------------------------------------------------------- /src/stories/DataVisualization/Datavisualization.mdx: -------------------------------------------------------------------------------- 1 | import { Meta, Source } from '@storybook/blocks'; 2 | import ClusterAggregates from './ClusterAggregates/ClusterAggregates'; 3 | 4 | 5 | 6 | # Data Visualization 7 | **react-azure-maps** provides a set of components to help you visualize data on a map like this:
8 | 9 | 10 | 11 | We will need two kinds of components to visualize data on a map: 12 | 1. ``: to provide your data to the map. 13 | 2. ``: to create a visualization layer on the map based on your data. 14 | 15 | Therefore, the basic structure of a data visualization component is as follows: 16 | 17 | { 21 | return ( 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | ); 30 | }; 31 | `} /> 32 | ## Create a data source 33 | To create a data source, you can pass the data to the `collection` prop in various ways:
34 | 35 | You can specify a location by passing a data point: 36 | ...`} 40 | /> 41 | Add more details via the `Feature` object like this:
42 | ...`} 46 | /> 47 | An array of data points is accepted:
48 | ...`} 57 | /> 58 | You can also provide the data by passing the `dataFromUrl` prop like this:
59 | 62 | ... 63 |
64 | `} /> 65 | 66 |
67 | Now that you have created a data source, you can create a visualization layer on the map.
68 | See examples below for further details. -------------------------------------------------------------------------------- /src/stories/DataVisualization/HeatMapLayer/HeatMapLayer.mdx: -------------------------------------------------------------------------------- 1 | import { Source, Meta } from '@storybook/blocks'; 2 | 3 | import HeatMapLayer from './HeatMapLayer'; 4 | 5 | 6 | 7 | # Heat Map Layer 8 | 9 | A heat map layer can be used to visualize the density of point data on the map.
10 | The following code shows how to add a simple heat map layer. A thorough tutorial can be found [here](https://learn.microsoft.com/en-us/azure/azure-maps/map-add-heat-map-layer).
11 | For more available properties, see the documentation [HeatMapLayerOptions](https://learn.microsoft.com/en-us/javascript/api/azure-maps-control/atlas.heatmaplayeroptions?view=azure-maps-typescript-latest). 12 | 13 | 14 | 15 | 24 | `}/> 25 | 26 | ## Color 27 | You can customize the color of the heat map layer by passing the `color` prop like this:
28 | 29 | 42 | 43 | 61 | `}/> 62 | 63 | ## Weight 64 | Specifies how much an individual data point contributes to the heatmap.
65 | Must be a number greater than 0. Default is `1`.
66 | In our example, we set the weight based on the "mag" property of the earthquake data.
67 | 68 | ; 81 | 82 | 95 | `}/> 96 | 97 | 98 | -------------------------------------------------------------------------------- /src/stories/DataVisualization/HeatMapLayer/HeatMapLayer.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react'; 2 | import HeatMapLayer from './HeatMapLayer'; 3 | 4 | const meta: Meta = { 5 | title: 'Data Visualization/Heat Map Layer', 6 | component: HeatMapLayer, 7 | parameters: { 8 | controls: { exclude: ['color'] }, 9 | storySource: { 10 | source: ` 11 | import { AzureMap, AzureMapsProvider, AzureMapDataSourceProvider, AzureMapLayerProvider } from 'react-azure-maps'; 12 | 13 | const magWeight = [ 14 | 'interpolate', 15 | ['exponential', 2], //Using an exponential interpolation since earthquake magnitudes are on an exponential scale. 16 | ['get', 'mag'], 17 | 0, 18 | 0, 19 | 10, 20 | 1, 21 | ]; 22 | 23 | const HeatMapLayer = () => { 24 | return ( 25 | 26 | 27 | 31 | 40 | 41 | 42 | 43 | ); 44 | }; 45 | `, 46 | }, 47 | }, 48 | args: { 49 | radius: 10, 50 | opacity: 1, 51 | intensity: 1, 52 | weight: false, 53 | }, 54 | argTypes: { 55 | radius: { control: { type: 'range', min: 1, max: 50, step: 1 } }, 56 | opacity: { control: { type: 'range', min: 0, max: 1, step: 0.1 } }, 57 | intensity: { control: { type: 'range', min: 0, max: 5, step: 0.1 } }, 58 | weight: { description: 'base on "mag" property' }, 59 | }, 60 | }; 61 | export default meta; 62 | 63 | type Story = StoryObj; 64 | 65 | export const Example: Story = {}; 66 | -------------------------------------------------------------------------------- /src/stories/DataVisualization/HeatMapLayer/HeatMapLayer.tsx: -------------------------------------------------------------------------------- 1 | import { mapOptions } from '../../../key'; 2 | import { AzureMap, AzureMapsProvider, AzureMapDataSourceProvider, AzureMapLayerProvider } from 'react-azure-maps'; 3 | 4 | export interface HeatMapLayerProps { 5 | radius?: number; 6 | opacity?: number; 7 | intensity?: number; 8 | weight?: boolean; 9 | color?: any; 10 | } 11 | 12 | const magWeight = [ 13 | 'interpolate', 14 | ['exponential', 2], //Using an exponential interpolation since earthquake magnitudes are on an exponential scale. 15 | ['get', 'mag'], 16 | 0, 17 | 0, 18 | 10, 19 | 1, 20 | ]; 21 | 22 | const HeatMapLayer = ({ radius, opacity, intensity, weight, color }: HeatMapLayerProps) => { 23 | return ( 24 | 25 |
26 | 27 | 31 | 41 | 42 | 43 |
44 |
45 | ); 46 | }; 47 | 48 | export default HeatMapLayer; 49 | -------------------------------------------------------------------------------- /src/stories/DataVisualization/ImageLayer/ImageLayer.mdx: -------------------------------------------------------------------------------- 1 | import { Source, Meta } from '@storybook/blocks'; 2 | import * as ImageLayerStories from './ImageLayer.stories.ts'; 3 | import ImageLayer from './ImageLayer'; 4 | 5 | 6 | 7 | # Image Layer 8 | 9 | You can overlay an image on a fixed set of coordinates with the Image Layer.
10 | The following code shows how to add a simple image layer. A thorough tutorial can be found [here](https://learn.microsoft.com/en-us/azure/azure-maps/map-add-image-layer).
11 | For more available properties, see the documentation [ImageLayerOptions](https://learn.microsoft.com/en-us/javascript/api/azure-maps-control/atlas.imagelayeroptions?view=azure-maps-typescript-latest). 12 | 13 | > Note that browsers might have difficulty loading a large image. In this case, consider breaking your image up into tiles and loading them into the map as a [TileLayer](/docs/data-visualization-tile-layer--docs). 14 | 15 | 16 | 17 | 32 | `}/> -------------------------------------------------------------------------------- /src/stories/DataVisualization/ImageLayer/ImageLayer.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react'; 2 | import ImageLayer from './ImageLayer'; 3 | 4 | const meta: Meta = { 5 | title: 'Data Visualization/Image Layer', 6 | component: ImageLayer, 7 | parameters: { 8 | storySource: { 9 | source: ` 10 | import { AzureMap, AzureMapsProvider, AzureMapDataSourceProvider, AzureMapLayerProvider } from 'react-azure-maps'; 11 | 12 | const ImageLayer = () => { 13 | 14 | return ( 15 | 16 | 17 | 18 | 36 | 37 | 38 | 39 | ); 40 | }; 41 | `, 42 | }, 43 | }, 44 | args: { 45 | opacity: 1, 46 | contrast: 0, 47 | saturation: 0, 48 | hueRotation: 0, 49 | fadeDuration: 300, 50 | }, 51 | argTypes: { 52 | opacity: { control: { type: 'range', min: 0, max: 1, step: 0.1 } }, 53 | contrast: { control: { type: 'range', min: -1, max: 1, step: 0.1 } }, 54 | saturation: { control: { type: 'range', min: -1, max: 1, step: 0.1 } }, 55 | hueRotation: { control: { type: 'range', min: 0, max: 360, step: 1 } }, 56 | fadeDuration: { control: { type: 'range', min: 0, max: 1000, step: 100 } }, 57 | }, 58 | }; 59 | export default meta; 60 | 61 | type Story = StoryObj; 62 | 63 | export const Example: Story = {}; 64 | -------------------------------------------------------------------------------- /src/stories/DataVisualization/ImageLayer/ImageLayer.tsx: -------------------------------------------------------------------------------- 1 | import { mapOptions } from '../../../key'; 2 | import { AzureMap, AzureMapsProvider, AzureMapDataSourceProvider, AzureMapLayerProvider } from 'react-azure-maps'; 3 | import { ImageLayerOptions } from 'azure-maps-control'; 4 | 5 | const ImageLayer = ({ opacity, contrast, saturation, hueRotation, fadeDuration }: ImageLayerOptions) => { 6 | return ( 7 | 8 |
9 | 10 | 11 | 29 | 30 | 31 |
32 |
33 | ); 34 | }; 35 | 36 | export default ImageLayer; 37 | -------------------------------------------------------------------------------- /src/stories/DataVisualization/LineLayer/LineLayer.mdx: -------------------------------------------------------------------------------- 1 | import { Source, Meta } from '@storybook/blocks'; 2 | 3 | import LineLayer from './LineLayer'; 4 | 5 | 6 | 7 | # Line Layer 8 | 9 | A line layer can be used to render LineString and MultiLineString features as paths or routes on the map.
10 | It can also be used to render the outline of Polygon and MultiPolygon features.

11 | The following code shows how to add a simple line layer. A thorough tutorial can be found [here](https://learn.microsoft.com/en-us/azure/azure-maps/map-add-line-layer).
12 | For more available properties, see the documentation [LineLayerOptions](https://learn.microsoft.com/en-us/javascript/api/azure-maps-control/atlas.linelayeroptions?view=azure-maps-typescript-latest). 13 | 14 | 15 | 16 | 28 | `}/> -------------------------------------------------------------------------------- /src/stories/DataVisualization/LineLayer/LineLayer.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react'; 2 | import LineLayer from './LineLayer'; 3 | 4 | const meta: Meta = { 5 | title: 'Data Visualization/Line Layer', 6 | component: LineLayer, 7 | parameters: { 8 | storySource: { 9 | source: ` 10 | import { AzureMap, AzureMapsProvider, AzureMapDataSourceProvider, AzureMapLayerProvider } from 'react-azure-maps'; 11 | import atlas from 'azure-maps-control'; 12 | 13 | // create a line string for the data source 14 | const collection = new atlas.data.LineString([ 15 | [-74.0039, 40.88029], 16 | [-87.583, 41.93497], 17 | [-105.20507, 39.77476], 18 | [-122.43164, 47.66538], 19 | ]); 20 | 21 | const LineLayer = () => { 22 | return ( 23 | 24 | 25 | 26 | 37 | 38 | 39 | 40 | ); 41 | }; 42 | `, 43 | }, 44 | }, 45 | args: { 46 | strokeColor: 'DodgerBlue', 47 | strokeWidth: 4, 48 | strokeOpacity: 1, 49 | blur: 0, 50 | lineCap: 'round', 51 | translate: [0, 0], 52 | }, 53 | argTypes: { 54 | strokeWidth: { control: { type: 'range', min: 0, max: 25, step: 1 } }, 55 | strokeOpacity: { control: { type: 'range', min: 0, max: 1, step: 0.1 } }, 56 | blur: { control: { type: 'range', min: 0, max: 1, step: 0.1 } }, 57 | lineCap: { control: { type: 'select' }, options: ['butt', 'round', 'square'] }, 58 | }, 59 | }; 60 | export default meta; 61 | 62 | type Story = StoryObj; 63 | 64 | export const Example: Story = {}; 65 | -------------------------------------------------------------------------------- /src/stories/DataVisualization/LineLayer/LineLayer.tsx: -------------------------------------------------------------------------------- 1 | import { mapOptions } from '../../../key'; 2 | import { AzureMap, AzureMapsProvider, AzureMapDataSourceProvider, AzureMapLayerProvider } from 'react-azure-maps'; 3 | import { LineLayerOptions } from 'azure-maps-control'; 4 | import atlas from 'azure-maps-control'; 5 | 6 | const collection = new atlas.data.LineString([ 7 | [-74.0039, 40.88029], 8 | [-87.583, 41.93497], 9 | [-105.20507, 39.77476], 10 | [-122.43164, 47.66538], 11 | ]); 12 | 13 | const LineLayer = ({ strokeColor, strokeWidth, strokeOpacity, blur, lineCap, translate }: LineLayerOptions) => { 14 | return ( 15 | 16 |
17 | 18 | 19 | 30 | 31 | 32 |
33 |
34 | ); 35 | }; 36 | 37 | export default LineLayer; 38 | -------------------------------------------------------------------------------- /src/stories/DataVisualization/PolygonExtrusion/PolygonExtrusion.mdx: -------------------------------------------------------------------------------- 1 | import { Source, Meta } from '@storybook/blocks'; 2 | 3 | import PolygonExtrusion from './PolygonExtrusion'; 4 | 5 | import * as PolygonExtrusionStories from './PolygonExtrusion.stories'; 6 | 7 | 8 | 9 | # Polygon Extrusion 10 | 11 | The polygon extrusion layer renders the areas of **Polygon** and **MultiPolygon** features as extruded shapes.
12 | The height and base properties of the polygon extrusion layer define the base distance from the ground and the height of the extruded shape in meters. 13 | 14 | 15 | 16 | 21 | `}/> -------------------------------------------------------------------------------- /src/stories/DataVisualization/PolygonExtrusion/PolygonExtrusion.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react'; 2 | import PolygonExtrusionLayer from './PolygonExtrusion'; 3 | 4 | const meta: Meta = { 5 | title: 'Data Visualization/Polygon Extrusion Layer', 6 | component: PolygonExtrusionLayer, 7 | parameters: { 8 | storySource: { 9 | source: ` 10 | import { AzureMap, AzureMapsProvider, AzureMapDataSourceProvider, AzureMapLayerProvider } from 'react-azure-maps'; 11 | import atlas, { ControlOptions } from 'azure-maps-control'; 12 | 13 | const collection = new atlas.data.Polygon([ 14 | [ 15 | [-122.132815, 47.636661], 16 | [-122.133631, 47.636676], 17 | [-122.133636, 47.63652], 18 | [-122.133523, 47.63651], 19 | [-122.133545, 47.636361], 20 | [-122.133759, 47.636361], 21 | [-122.133759, 47.636173], 22 | [-122.132703, 47.63617], 23 | [-122.132713, 47.636343], 24 | [-122.13303, 47.636347], 25 | [-122.133035, 47.636528], 26 | [-122.13281, 47.636528], 27 | [-122.132815, 47.636661], 28 | ], 29 | ]); 30 | 31 | const PolygonExtrusionLayer = () => { 32 | return ( 33 | 34 | 44 | 45 | ; 55 | 56 | 57 | 58 | ); 59 | }; 60 | `, 61 | }, 62 | }, 63 | args: { 64 | height: 50, 65 | base: 0, 66 | fillColor: 'red', 67 | fillOpacity: 0.7, 68 | translate: [0, 0], 69 | }, 70 | }; 71 | export default meta; 72 | 73 | type Story = StoryObj; 74 | export const Example: Story = {}; 75 | -------------------------------------------------------------------------------- /src/stories/DataVisualization/PolygonExtrusion/PolygonExtrusion.tsx: -------------------------------------------------------------------------------- 1 | import { mapOptions } from '../../../key'; 2 | import { AzureMap, AzureMapsProvider, AzureMapDataSourceProvider, AzureMapLayerProvider } from 'react-azure-maps'; 3 | import { PolygonExtrusionLayerOptions, ControlOptions } from 'azure-maps-control'; 4 | import atlas from 'azure-maps-control'; 5 | 6 | const collection = new atlas.data.Polygon([ 7 | [ 8 | [-122.132815, 47.636661], 9 | [-122.133631, 47.636676], 10 | [-122.133636, 47.63652], 11 | [-122.133523, 47.63651], 12 | [-122.133545, 47.636361], 13 | [-122.133759, 47.636361], 14 | [-122.133759, 47.636173], 15 | [-122.132703, 47.63617], 16 | [-122.132713, 47.636343], 17 | [-122.13303, 47.636347], 18 | [-122.133035, 47.636528], 19 | [-122.13281, 47.636528], 20 | [-122.132815, 47.636661], 21 | ], 22 | ]); 23 | 24 | const PolygonExtrusionLayer = ({ height, base, fillColor, fillOpacity, translate }: PolygonExtrusionLayerOptions) => { 25 | return ( 26 | 27 |
28 | 38 | 39 | 43 | 44 | 45 |
46 |
47 | ); 48 | }; 49 | 50 | export default PolygonExtrusionLayer; 51 | -------------------------------------------------------------------------------- /src/stories/DataVisualization/PolygonLayer/PolygonLayer.mdx: -------------------------------------------------------------------------------- 1 | import PolygonLayer from './PolygonLayer'; 2 | import { Source, Meta } from '@storybook/addon-docs/blocks'; 3 | import * as PolygonLayerStories from './PolygonLayer.stories'; 4 | 5 | 6 | 7 | # Polygon Layer 8 | 9 | To create a polygon, add it to a data source and render it with a polygon layer using the PolygonLayer class. 10 | 11 | 12 | 14 | `}/> -------------------------------------------------------------------------------- /src/stories/DataVisualization/PolygonLayer/PolygonLayer.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react'; 2 | import PolygonLayer from './PolygonLayer'; 3 | 4 | const meta: Meta = { 5 | title: 'Data Visualization/Polygon Layer', 6 | component: PolygonLayer, 7 | parameters: { 8 | storySource: { 9 | source: ` 10 | import { AzureMap, AzureMapsProvider, AzureMapDataSourceProvider, AzureMapLayerProvider } from 'react-azure-maps'; 11 | import atlas from 'azure-maps-control'; 12 | 13 | const collection = new atlas.data.Polygon([ 14 | [ 15 | [-15.82031, 2.46018], 16 | [14.0625, 30.14512], 17 | [40.78125, 2.81137], 18 | [12.30468, 65.21989], 19 | [-15.82031, 2.46018], 20 | ], 21 | ]); 22 | 23 | const PolygonLayer = () => { 24 | 25 | return ( 26 | 27 | 28 | 29 | 31 | 32 | 33 | 34 | ); 35 | }; 36 | `, 37 | }, 38 | }, 39 | args: { 40 | fillColor: 'rgba(0, 0, 255, 0.5)', 41 | fillOpacity: 0.8, 42 | }, 43 | argTypes: { 44 | fillOpacity: { 45 | control: { type: 'range', min: 0, max: 1, step: 0.1 }, 46 | }, 47 | }, 48 | }; 49 | 50 | export default meta; 51 | 52 | export const Example: StoryObj = {}; 53 | -------------------------------------------------------------------------------- /src/stories/DataVisualization/PolygonLayer/PolygonLayer.tsx: -------------------------------------------------------------------------------- 1 | import { mapOptions } from '../../../key'; 2 | import { AzureMap, AzureMapsProvider, AzureMapDataSourceProvider, AzureMapLayerProvider } from 'react-azure-maps'; 3 | import { PolygonLayerOptions } from 'azure-maps-control'; 4 | import atlas from 'azure-maps-control'; 5 | 6 | const collection = new atlas.data.Polygon([ 7 | [ 8 | [-15.82031, 2.46018], 9 | [14.0625, 30.14512], 10 | [40.78125, 2.81137], 11 | [12.30468, 65.21989], 12 | [-15.82031, 2.46018], 13 | ], 14 | ]); 15 | 16 | const PolygonLayer = ({ fillColor, fillOpacity }: PolygonLayerOptions) => { 17 | return ( 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | ); 28 | }; 29 | 30 | export default PolygonLayer; 31 | -------------------------------------------------------------------------------- /src/stories/DataVisualization/SymbolLayer/SymbolLayer.mdx: -------------------------------------------------------------------------------- 1 | import { Source, Meta } from '@storybook/addon-docs/blocks'; 2 | import SymbolLayer from './SymbolLayer'; 3 | import * as SymbolLayerStories from './SymbolLayer.stories'; 4 | 5 | 6 | 7 | # Symbol Layer 8 | 9 | Connect a symbol to a data source, and use it to render an icon or a text at a given point. Symbol layers are rendered using WebGL. 10 | 11 | Use a symbol layer to render large collections of points on the map. Compared to HTML markers, the symbol layer renders a large number of point data on the map, with better performance. However, the symbol layer doesn't support traditional CSS and HTML elements for styling. 12 | 13 | 14 | 15 | You can customize the style of both icons and texts of a symbol layer by providing the `iconOptions` and `textOptions` properties. 16 | 17 | The following code shows how to add a simple symbol layer. A thorough tutorial can be found [here](https://learn.microsoft.com/en-us/azure/azure-maps/map-add-pin). 18 | For more available properties, see the documentation [SymbolLayerOptions](https://learn.microsoft.com/en-us/javascript/api/azure-maps-control/atlas.symbollayeroptions?view=azure-maps-typescript-latest). 19 | `}/> -------------------------------------------------------------------------------- /src/stories/DataVisualization/SymbolLayer/SymbolLayer.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react'; 2 | import SymbolLayer from './SymbolLayer'; 3 | 4 | const meta: Meta = { 5 | title: 'Data Visualization/Symbol Layer', 6 | component: SymbolLayer, 7 | parameters: {}, 8 | args: { 9 | image: 'pin-round-blue', 10 | optionsSize: 1.3, 11 | optionsAnchor: 'center', 12 | optionsOffset: [0, 0], 13 | }, 14 | }; 15 | export default meta; 16 | 17 | type Story = StoryObj; 18 | 19 | export const IconOptions: Story = { 20 | parameters: { 21 | controls: { exclude: ['font', 'textSize', 'textOffset', 'textAnchor', 'color', 'haloColor', 'haloWidth'] }, 22 | storySource: { 23 | source: ` 24 | import { AzureMap, AzureMapsProvider, AzureMapDataSourceProvider, AzureMapLayerProvider } from 'react-azure-maps'; 25 | import atlas from 'azure-maps-control'; 26 | 27 | const collection = generateRandomPoints(); 28 | 29 | const SymbolLayer = () => { 30 | 31 | return ( 32 | 33 | 34 | 35 | 49 | 50 | 51 | 52 | ); 53 | }; 54 | 55 | function generateRandomPoints() { 56 | var layerData = []; 57 | 58 | for (var i = 0; i < 50; i++) { 59 | layerData.push( 60 | new atlas.data.Feature(new atlas.data.Point([Math.random() * 360 - 180, Math.random() * 170 - 85]), { 61 | title: 'Pin_' + i, 62 | }), 63 | ); 64 | } 65 | 66 | return layerData; 67 | } 68 | `, 69 | }, 70 | }, 71 | argTypes: { 72 | image: { 73 | control: { type: 'select' }, 74 | options: [ 75 | 'marker-black', 76 | 'marker-blue', 77 | 'marker-darkblue', 78 | 'marker-red', 79 | 'marker-yellow', 80 | 'pin-blue', 81 | 'pin-darkblue', 82 | 'pin-red', 83 | 'pin-round-blue', 84 | 'pin-round-darkblue', 85 | 'pin-round-red', 86 | ], 87 | }, 88 | optionsSize: { control: { type: 'range', min: 0, max: 2, step: 0.1 } }, 89 | optionsAnchor: { 90 | control: { type: 'select' }, 91 | options: ['center', 'top', 'bottom', 'left', 'right', 'top-left', 'top-right', 'bottom-left', 'bottom-right'], 92 | }, 93 | }, 94 | }; 95 | 96 | export const TextOptions: Story = { 97 | parameters: { 98 | controls: { exclude: ['image', 'opacity', 'optionsSize', 'optionsAnchor', 'optionsOffset'] }, 99 | storySource: { 100 | source: ` 101 | import { AzureMap, AzureMapsProvider, AzureMapDataSourceProvider, AzureMapLayerProvider } from 'react-azure-maps'; 102 | import atlas from 'azure-maps-control'; 103 | 104 | const collection = generateRandomPoints(); 105 | 106 | const SymbolLayer = () => { 107 | 108 | return ( 109 | 110 | 111 | 112 | 132 | 133 | 134 | 135 | ); 136 | }; 137 | 138 | function generateRandomPoints() { 139 | var layerData = []; 140 | 141 | for (var i = 0; i < 50; i++) { 142 | layerData.push( 143 | new atlas.data.Feature(new atlas.data.Point([Math.random() * 360 - 180, Math.random() * 170 - 85]), { 144 | title: 'Pin_' + i, 145 | }), 146 | ); 147 | } 148 | 149 | return layerData; 150 | } 151 | `, 152 | }, 153 | }, 154 | args: { 155 | font: 'StandardFont-Bold', 156 | textSize: 12, 157 | textOffset: [0, 2], 158 | textAnchor: 'center', 159 | color: 'black', 160 | haloColor: 'white', 161 | haloWidth: 1, 162 | }, 163 | argTypes: { 164 | font: { control: { type: 'select' }, options: ['SegoeUi-Bold', 'SegoeUi-Regular', 'SegoeUi-Light'] }, 165 | textSize: { name: 'size', control: { type: 'range', min: 0, max: 25, step: 1 } }, 166 | textOffset: { name: 'offset' }, 167 | textAnchor: { 168 | name: 'anchor', 169 | control: { type: 'select' }, 170 | options: ['center', 'top', 'bottom', 'left', 'right', 'top-left', 'top-right', 'bottom-left', 'bottom-right'], 171 | }, 172 | color: { control: { type: 'color' } }, 173 | haloColor: { control: { type: 'color' } }, 174 | haloWidth: { control: { type: 'range', min: 0, max: 15, step: 0.5 } }, 175 | }, 176 | }; 177 | -------------------------------------------------------------------------------- /src/stories/DataVisualization/SymbolLayer/SymbolLayer.tsx: -------------------------------------------------------------------------------- 1 | import { mapOptions } from '../../../key'; 2 | import { AzureMap, AzureMapsProvider, AzureMapDataSourceProvider, AzureMapLayerProvider } from 'react-azure-maps'; 3 | import atlas from 'azure-maps-control'; 4 | 5 | const collection = generateRandomPoints(); 6 | export interface SymbolLayerProps { 7 | image?: string; 8 | optionsSize?: number; 9 | optionsAnchor?: string; 10 | optionsOffset?: number[]; 11 | font?: string; 12 | textSize?: number; 13 | textOffset?: number[]; 14 | textAnchor?: string; 15 | color?: string; 16 | haloColor?: string; 17 | haloWidth?: number; 18 | } 19 | 20 | const SymbolLayer = ({ 21 | image, 22 | optionsSize, 23 | optionsAnchor, 24 | optionsOffset, 25 | font, 26 | textSize, 27 | textOffset, 28 | textAnchor, 29 | color, 30 | haloColor, 31 | haloWidth, 32 | }: SymbolLayerProps) => { 33 | return ( 34 | 35 |
36 | 37 | 38 | 58 | 59 | 60 |
61 |
62 | ); 63 | }; 64 | 65 | function generateRandomPoints() { 66 | var layerData = []; 67 | 68 | for (var i = 0; i < 50; i++) { 69 | layerData.push( 70 | new atlas.data.Feature(new atlas.data.Point([Math.random() * 360 - 180, Math.random() * 170 - 85]), { 71 | title: 'Pin_' + i, 72 | }), 73 | ); 74 | } 75 | 76 | return layerData; 77 | } 78 | 79 | export default SymbolLayer; 80 | -------------------------------------------------------------------------------- /src/stories/DataVisualization/TileLayer/TileLayer.mdx: -------------------------------------------------------------------------------- 1 | import { Source, Meta } from '@storybook/blocks'; 2 | import * as TileLayerStories from './TileLayer.stories.ts'; 3 | 4 | 5 | 6 | # Tile Layer 7 | 8 | Tile layers allow you to superimpose images on top of Azure Maps base map tiles. 9 | The following code shows how to add a simple tile layer. A thorough tutorial can be found [here](https://learn.microsoft.com/en-us/azure/azure-maps/map-add-tile-layer).
10 | For more available properties, see the documentation [TileLayerOptions](https://learn.microsoft.com/en-us/javascript/api/azure-maps-control/atlas.tilelayeroptions?view=azure-maps-typescript-latest). 11 | 12 | 20 | `}/> -------------------------------------------------------------------------------- /src/stories/DataVisualization/TileLayer/TileLayer.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react'; 2 | import TileLayer from './TileLayer'; 3 | 4 | const meta: Meta = { 5 | title: 'Data Visualization/Tile Layer', 6 | component: TileLayer, 7 | parameters: { 8 | storySource: { 9 | source: ` 10 | import { AzureMap, AzureMapsProvider, AzureMapDataSourceProvider, AzureMapLayerProvider } from 'react-azure-maps'; 11 | 12 | const TileLayer = () => { 13 | 14 | return ( 15 | 16 | 17 | 18 | 27 | 28 | 29 | 30 | ); 31 | }; 32 | `, 33 | }, 34 | }, 35 | args: { 36 | bounds: [-50, -20, 50, 20], 37 | tileSize: 50, 38 | }, 39 | }; 40 | export default meta; 41 | 42 | type Story = StoryObj; 43 | export const Example: Story = {}; 44 | -------------------------------------------------------------------------------- /src/stories/DataVisualization/TileLayer/TileLayer.tsx: -------------------------------------------------------------------------------- 1 | import { mapOptions } from '../../../key'; 2 | import { AzureMap, AzureMapsProvider, AzureMapDataSourceProvider, AzureMapLayerProvider } from 'react-azure-maps'; 3 | import { TileLayerOptions } from 'azure-maps-control'; 4 | 5 | const TileLayer = ({ bounds = [-50, -20, 50, 20], tileSize = 50 }: TileLayerOptions) => { 6 | return ( 7 | 8 |
9 | 10 | 11 | 20 | 21 | 22 |
23 |
24 | ); 25 | }; 26 | 27 | export default TileLayer; 28 | -------------------------------------------------------------------------------- /src/stories/DefaultMap/DefaultMap.tsx: -------------------------------------------------------------------------------- 1 | import { AzureMap, AzureMapsProvider, IAzureMapOptions, IAzureMapControls } from 'react-azure-maps'; 2 | import { mapOptions } from '../../key'; 3 | 4 | export interface DefaultMapProps { 5 | options?: IAzureMapOptions; 6 | controls?: IAzureMapControls[]; 7 | } 8 | 9 | const DefaultMap = ({ options, controls }: DefaultMapProps) => { 10 | return ( 11 | 12 |
13 | 14 |
15 |
16 | ); 17 | }; 18 | 19 | export default DefaultMap; 20 | -------------------------------------------------------------------------------- /src/stories/DefaultMap/GettingStarted.mdx: -------------------------------------------------------------------------------- 1 | import { Meta, Source } from '@storybook/addon-docs/blocks'; 2 | 3 | import DefaultMap from './DefaultMap'; 4 | 5 | 6 | 7 | 8 | # Getting Started 9 | 10 | Welcome!
11 | **react-azure-maps** is the React wrapper for [Azure Maps](https://azure.microsoft.com/pl-pl/services/azure-maps/).
12 | This guide will help you get started with it and create your first map. 13 | 14 | ## Table of contents 15 | - [Installation](#installation) 16 | - [Styling](#styling) 17 | - [Create a map](#create-a-map) 18 | - [Authentication](#authentication) 19 | - [Introduction](#introduction) 20 | 21 | ## Installation 22 | 23 | Use the package manager `npm` or `yarn` 24 | 25 | ```bash 26 | npm install react-azure-maps 27 | ``` 28 | 29 | ```bash 30 | yarn add react-azure-maps 31 | ``` 32 | 33 | ## Styling 34 | Embed the following CSS into your application.
35 | The stylesheet is required for the marker, popup, and control components in `react-azure-maps` to work properly. 36 | ```javascript 37 | import 'azure-maps-control/dist/atlas.min.css' 38 | ``` 39 | ## Create a map 40 | Let's create a simple map with the default style. 41 | 42 | {/* show default map */} 43 | 44 | ( 55 |
56 | 57 | 58 | 59 |
60 | ); 61 | `} /> 62 | 63 | ## Authentication 64 | 65 | Update the `authOptions` property in the option to authenticate with Azure Maps.
66 | The subscription key is intended for **development environments** only and must not be utilized in a production application. 67 | #### AAD 68 | ```javascript 69 | authOptions: { 70 | authType: AuthenticationType.aad, 71 | clientId: '...', 72 | aadAppId: '...', 73 | aadTenant: '...' 74 | } 75 | ``` 76 | #### Anonymous 77 | ```javascript 78 | authOptions: { 79 | authType: AuthenticationType.anonymous, 80 | clientId: '...', 81 | getToken: (resolve, reject) => { 82 | // URL to your authentication service that retrieves an Azure Active Directory Token. 83 | var tokenServiceUrl = "https://example.com/api/GetAzureMapsToken"; 84 | fetch(tokenServiceUrl).then(r => r.text()).then(token => resolve(token)); 85 | } 86 | } 87 | ``` 88 | 89 | #### SAS Token 90 | ```javascript 91 | authOptions: { 92 | authType: AuthenticationType.sas, 93 | getToken: (resolve, reject) => { 94 | // URL to your authentication service that retrieves a SAS Token. 95 | var tokenServiceUrl = "https://example.com/api/GetSASToken"; 96 | fetch(tokenServiceUrl).then(r => r.text()).then(token => resolve(token)); 97 | } 98 | } 99 | ``` 100 | ## Introduction 101 | The library is implemented under the hood on `Contexts`.
102 | There are three main references that depend on the basic `Azure Maps API`: 103 | #### AzureMapProvider 104 | - Provides the map instance and references to the map. 105 | - See [Default Map](#create-a-map) as an example. 106 | #### AzureMapDataSourceProvider 107 | - Provides the data source instance and references to the data source. 108 | - See [Data Visualization Introduction](/docs/data-visualization-introduction--docs) for more usages. 109 | #### AzureMapLayerProvider 110 | - Provides the layer instance and references to the layer. 111 | - See [Data Visualization](/docs/data-visualization-introduction--docs) for more usages. -------------------------------------------------------------------------------- /src/stories/Events/HtmlMarkerEvents/HtmlMarkerEvents.css: -------------------------------------------------------------------------------- 1 | .circle-marker.blink { 2 | animation: blink-animation 0.7s infinite alternate; 3 | } 4 | 5 | @keyframes blink-animation { 6 | 0% { 7 | opacity: 0.5; 8 | background-color: rgb(235, 167, 167); 9 | box-shadow: 0 0 0 0 rgba(162, 4, 44, 0); 10 | } 11 | 20% { 12 | opacity: 0.7; 13 | background-color: rgb(235, 167, 167); 14 | box-shadow: 0 0 0 20px rgba(162, 4, 44, 0); 15 | } 16 | 100% { 17 | opacity: 1; 18 | background-color: crimson; 19 | box-shadow: 0 0 0 0 rgba(162, 4, 44, 0.6); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/stories/Events/HtmlMarkerEvents/HtmlMarkerEvents.mdx: -------------------------------------------------------------------------------- 1 | import { Meta, Source } from '@storybook/addon-docs/blocks'; 2 | import HtmlMarkerEvents from './HtmlMarkerEvents'; 3 | 4 | 5 | 6 | # HTML Marker Events 7 | 8 | The HTML Marker component triggers events whenever the user interacts with it.
9 | For a complete list of available events, refer to the [HTML Marker Events Sample](https://samples.azuremaps.com/html-markers/html-marker-layer-events). 10 | 11 | Below is an example of how to listen for drag events on an HTML marker.
12 | In this example, CSS animations are used to make the marker blink while it is being dragged. 13 | **Try dragging the markers** to see the effect. 14 | 15 | 16 | 17 | 35 | ); 36 | 37 | const LayerEvents = () => { 38 | // Add class name to the marker to apply CSS animation 39 | const startBlink = (e: any) => { 40 | // Access the marker through the event object 41 | e.target.element.firstElementChild.className = 'circle-marker blink'; 42 | }; 43 | const stopBlink = (e: any) => { 44 | e.target.element.firstElementChild.className = 'circle-marker'; 45 | }; 46 | 47 | return ( 48 | 49 | 50 | {collection.map((point: number[], index) => ( 51 | 66 | ))} 67 | 68 | 69 | ); 70 | }; 71 | 72 | function generateRandomPoints() { 73 | var layerData = []; 74 | 75 | for (var i = 0; i < 30; i++) { 76 | layerData.push([Math.random() * 360 - 180, Math.random() * 170 - 85]); 77 | } 78 | 79 | return layerData; 80 | } 81 | `}/> 82 | 83 | 108 | -------------------------------------------------------------------------------- /src/stories/Events/HtmlMarkerEvents/HtmlMarkerEvents.tsx: -------------------------------------------------------------------------------- 1 | import { mapOptions } from '../../../key'; 2 | import { AzureMap, AzureMapsProvider, AzureMapHtmlMarker } from 'react-azure-maps'; 3 | import './HtmlMarkerEvents.css'; 4 | 5 | // generate random points 6 | const collection = generateRandomPoints(); 7 | 8 | // content for the html marker 9 | const circleMarker = ( 10 |
19 | ); 20 | 21 | const LayerEvents = () => { 22 | // add class name to the marker to apply css animation 23 | const startBlink = (e: any) => { 24 | // access the marker through the event object 25 | e.target.element.firstElementChild.className = 'circle-marker blink'; 26 | }; 27 | const stopBlink = (e: any) => { 28 | e.target.element.firstElementChild.className = 'circle-marker'; 29 | }; 30 | 31 | return ( 32 |
33 | 34 | 35 | {collection.map((point: number[], index) => ( 36 | 51 | ))} 52 | 53 | 54 |
55 | ); 56 | }; 57 | 58 | function generateRandomPoints() { 59 | var layerData = []; 60 | 61 | for (var i = 0; i < 30; i++) { 62 | layerData.push([Math.random() * 360 - 180, Math.random() * 170 - 85]); 63 | } 64 | 65 | return layerData; 66 | } 67 | 68 | export default LayerEvents; 69 | -------------------------------------------------------------------------------- /src/stories/Events/LayerEvents/LayerEvents.mdx: -------------------------------------------------------------------------------- 1 | import { Meta, Source } from '@storybook/addon-docs/blocks'; 2 | import LayerEvents from './LayerEvents'; 3 | 4 | 5 | 6 | # Layer Events 7 | The layer triggers events whenever the user interacts with it.
8 | For a complete list of available events, refer to the [Layer Events Sample](https://samples.azuremaps.com/symbol-layer/symbol-layer-events). 9 | 10 | > The symbol, bubble, line, and polygon layer all support the same set of events.
11 | > The heat map and tile layers don't support any of these events. 12 | 13 | The example below shows how to listen for different mouse events on a bubble layer.
14 | **Try moving the mouse back and forth over the map**.
15 | You'll see the bubbles' color change as the mouse enters and leaves. 16 | 17 | 18 | 19 | { 28 | const [color, setColor] = useState('red'); 29 | 30 | const setRandomColor = () => { 31 | // Generate a random color 32 | const randomHex = Math.floor(Math.random() * 16777215).toString(16); 33 | setColor(\`#\${randomHex.padStart(6, '0')}\`); 34 | }; 35 | 36 | return ( 37 | 38 | 39 | 40 | 51 | 52 | 53 | 54 | ); 55 | }; 56 | 57 | function generateRandomPoints() { 58 | var layerData = []; 59 | 60 | for (var i = 0; i < 50; i++) { 61 | layerData.push( 62 | new atlas.data.Feature(new atlas.data.Point([Math.random() * 360 - 180, Math.random() * 170 - 85]), { 63 | title: 'Pin_' + i, 64 | }), 65 | ); 66 | } 67 | 68 | return layerData; 69 | } 70 | `}/> 71 | 72 | -------------------------------------------------------------------------------- /src/stories/Events/LayerEvents/LayerEvents.tsx: -------------------------------------------------------------------------------- 1 | import { mapOptions } from '../../../key'; 2 | import { AzureMap, AzureMapsProvider, AzureMapDataSourceProvider, AzureMapLayerProvider } from 'react-azure-maps'; 3 | import atlas from 'azure-maps-control'; 4 | import { useState } from 'react'; 5 | 6 | const collection = generateRandomPoints(); 7 | 8 | const LayerEvents = () => { 9 | const [color, setColor] = useState('red'); 10 | 11 | const setRandomColor = () => { 12 | const randomHex = Math.floor(Math.random() * 16777215).toString(16); 13 | setColor(`#${randomHex.padStart(6, '0')}`); 14 | }; 15 | 16 | return ( 17 |
18 | 19 | 20 | 21 | 32 | 33 | 34 | 35 |
36 | ); 37 | }; 38 | 39 | function generateRandomPoints() { 40 | var layerData = []; 41 | 42 | for (var i = 0; i < 50; i++) { 43 | layerData.push( 44 | new atlas.data.Feature(new atlas.data.Point([Math.random() * 360 - 180, Math.random() * 170 - 85]), { 45 | title: 'Pin_' + i, 46 | }), 47 | ); 48 | } 49 | 50 | return layerData; 51 | } 52 | 53 | export default LayerEvents; 54 | -------------------------------------------------------------------------------- /src/stories/Events/MapEvents/MapEvents.mdx: -------------------------------------------------------------------------------- 1 | import MapEvents from './MapEvents'; 2 | import { Meta, Source } from '@storybook/addon-docs/blocks'; 3 | 4 | 5 | # Map Events 6 | 7 | The Map component triggers events whenever the user interacts with the map.
8 | For a complete list of available events, refer to the [Map Events Sample](https://samples.azuremaps.com/map/map-events). 9 | 10 | The example below shows how to listen for a click event.
11 | When the user clicks on the map, a popup will display the coordinates of the clicked location. 12 | 13 | 14 | 15 | { 20 | const [position, setPosition] = useState([0, 0]); 21 | 22 | const handleMapClick = (e: any) => { 23 | // Get the position of the click event 24 | setPosition(e.position); 25 | }; 26 | const displayPosition = (position: number[]) => { 27 | // Format position as a string 28 | // Round to 4 decimal places 29 | const longitude = \`\${Number(position[0]).toFixed(4)} °\${position[0] > 0 ? 'E' : 'W'}\`; 30 | const latitude = \`\${Number(position[1]).toFixed(4)} °\${position[1] > 0 ? 'N' : 'S'}\`; 31 | return \`\${longitude}, \${latitude}\`; 32 | }; 33 | 34 | return ( 35 | 36 | 37 | {displayPosition(position)}} 41 | /> 42 | 43 | 44 | ); 45 | }; 46 | `}/> -------------------------------------------------------------------------------- /src/stories/Events/MapEvents/MapEvents.tsx: -------------------------------------------------------------------------------- 1 | import { AzureMap, AzureMapsProvider, AzureMapPopup } from 'react-azure-maps'; 2 | import { useState } from 'react'; 3 | import { mapOptions } from '../../../key'; 4 | 5 | const MapEvents = () => { 6 | const [position, setPosition] = useState([0, 0]); 7 | 8 | const handleMapClick = (e: any) => { 9 | setPosition(e.position); 10 | }; 11 | const displayPosition = (position: number[]) => { 12 | // Format position as a string 13 | // round to 4 decimal places 14 | const longitude = `${Number(position[0]).toFixed(4)} °${position[0] > 0 ? 'E' : 'W'}`; 15 | const latitude = `${Number(position[1]).toFixed(4)} °${position[1] > 0 ? 'N' : 'S'}`; 16 | return `${longitude}, ${latitude}`; 17 | }; 18 | 19 | return ( 20 |
21 | 22 | 23 | {displayPosition(position)}
} 27 | /> 28 |
29 | 30 | 31 | ); 32 | }; 33 | 34 | export default MapEvents; 35 | -------------------------------------------------------------------------------- /src/stories/MapAnnotations/HtmlMarker/HtmlMarker.mdx: -------------------------------------------------------------------------------- 1 | import { Meta, Source } from '@storybook/blocks'; 2 | 3 | import * as HtmlMarkerStories from './HtmlMarker.stories'; 4 | 5 | import HtmlMarker from './HtmlMarker'; 6 | 7 | 8 | 9 | # HTML Marker 10 | The HtmlMarker class has a default style.
11 | The following is a simple example of how to use the HtmlMarker component.
12 | You can customize the marker by setting the [options prop](https://learn.microsoft.com/en-us/javascript/api/azure-maps-control/atlas.htmlmarkeroptions?view=azure-maps-typescript-latest). 13 | 14 | 15 | 16 | 18 | `} /> 19 | 20 | You can also pass a **custom component** to the marker.
21 | Note that the custom component will be rendered into a static element. 22 | } /> 24 | `} /> -------------------------------------------------------------------------------- /src/stories/MapAnnotations/HtmlMarker/HtmlMarker.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react'; 2 | import HtmlMarker from './HtmlMarker'; 3 | 4 | const meta: Meta = { 5 | title: 'Map Annotations/HTML Marker', 6 | component: HtmlMarker, 7 | args: { 8 | color: 'DodgerBlue', 9 | text: '10', 10 | position: [0, 0], 11 | draggable: false, 12 | }, 13 | parameters: { 14 | storySource: { 15 | source: ` 16 | import { AzureMap, AzureMapHtmlMarker, AzureMapsProvider } from 'react-azure-maps'; 17 | import { HtmlMarkerOptions } from 'azure-maps-control'; 18 | 19 | const HtmlMarker = () => { 20 | return ( 21 | 22 | 23 | 30 | 31 | 32 | ); 33 | }; 34 | `, 35 | }, 36 | }, 37 | }; 38 | 39 | export default meta; 40 | 41 | type Story = StoryObj; 42 | 43 | export const Example: Story = {}; 44 | -------------------------------------------------------------------------------- /src/stories/MapAnnotations/HtmlMarker/HtmlMarker.tsx: -------------------------------------------------------------------------------- 1 | import { AzureMap, AzureMapHtmlMarker, AzureMapsProvider } from 'react-azure-maps'; 2 | import { HtmlMarkerOptions } from 'azure-maps-control'; 3 | import { mapOptions } from '../../../key'; 4 | 5 | const HtmlMarker = ({ color, text, position, draggable }: HtmlMarkerOptions) => { 6 | return ( 7 | 8 |
9 | 10 | 18 | 19 |
20 |
21 | ); 22 | }; 23 | 24 | export default HtmlMarker; 25 | -------------------------------------------------------------------------------- /src/stories/MapAnnotations/Popup/Accessible/AccessiblePopup.mdx: -------------------------------------------------------------------------------- 1 | import { Meta, Source } from '@storybook/blocks'; 2 | 3 | import AccessiblePopup from './AccessiblePopup'; 4 | 5 | 6 | 7 | # Accessible Popup 8 | This sample shows how to use popups in a way that users can easily access them using keyboard shortcuts (tab). 9 |
Use Tab to navigate.
10 | 11 |
12 | { 24 | const randomLongitude = Math.floor(Math.random() * 60 - 30); 25 | const randomLatitude = Math.floor(Math.random() * 40 - 20); 26 | return new data.Position(randomLongitude, randomLatitude); 27 | }); 28 | 29 | const AccessiblePopups = () => { 30 | 31 | return ( 32 | 33 |
34 | 35 | 37 | 46 | { 47 | points.map( 48 | (coordinates, idx) => 49 | 54 | ) 55 | } 56 | 57 | <> 58 | {points.map((point, idx) => 59 | { \`Lng: \${point[0]}, Lat: \${point[1]}\` }
68 | } 69 | /> 70 | )} 71 | 72 |
73 | 74 | 75 | ); 76 | } 77 | 78 | export default AccessiblePopups; 79 | `} /> -------------------------------------------------------------------------------- /src/stories/MapAnnotations/Popup/Accessible/AccessiblePopup.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | AzureMap, 3 | AzureMapDataSourceProvider, 4 | AzureMapFeature, 5 | AzureMapLayerProvider, 6 | AzureMapsProvider, 7 | AzureMapPopup, 8 | } from 'react-azure-maps'; 9 | import { data } from 'azure-maps-control'; 10 | import { mapOptions } from '../../../../key'; 11 | 12 | const points = Array.from({ length: 10 }).map(() => { 13 | const randomLongitude = Math.floor(Math.random() * 60 - 30); 14 | const randomLatitude = Math.floor(Math.random() * 40 - 20); 15 | return new data.Position(randomLongitude, randomLatitude); 16 | }); 17 | 18 | const AccessiblePopups = () => { 19 | 20 | return ( 21 | 22 |
23 | 24 | 26 | 35 | { 36 | points.map( 37 | (coordinates, idx) => 38 | 39 | ) 40 | } 41 | 42 | <> 43 | {points.map((point, idx) => 44 | { `Lng: ${point[0]}, Lat: ${point[1]}` }
53 | } 54 | /> 55 | )} 56 | 57 |
58 | 59 | 60 | ); 61 | } 62 | 63 | export default AccessiblePopups; -------------------------------------------------------------------------------- /src/stories/MapAnnotations/Popup/Basic/Popup.mdx: -------------------------------------------------------------------------------- 1 | import { Meta, Source } from '@storybook/blocks'; 2 | 3 | import * as PopupStories from './Popup.stories'; 4 | 5 | import Popup from './Popup'; 6 | 7 | 8 | 9 | # Popup 10 | A popup is an information window anchored at a specified position on a map. 11 | With **react-azure-maps**, you can quickly customize the `Popup` component with these 3 props: 12 | 13 | {/* show basic code */} 14 | 15 | Hello World} 20 | /> 21 | `} /> -------------------------------------------------------------------------------- /src/stories/MapAnnotations/Popup/Basic/Popup.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react'; 2 | import Popup from './Popup'; 3 | 4 | const meta: Meta = { 5 | title: 'Map Annotations/Popup', 6 | component: Popup, 7 | args: { 8 | isVisible: true, 9 | options: { 10 | position: [0, 0], 11 | }, 12 | }, 13 | parameters: { 14 | storySource: { 15 | source: ` 16 | import { AzureMap, AzureMapsProvider, AzureMapPopup, IAzureMapPopup } from 'react-azure-maps'; 17 | 18 | const Popup = () => { 19 | 20 | return ( 21 | 22 | 23 | Hello World} 27 | /> 28 | 29 | 30 | ); 31 | }; 32 | `, 33 | }, 34 | }, 35 | }; 36 | 37 | export default meta; 38 | 39 | type Story = StoryObj; 40 | 41 | export const Example: Story = {}; 42 | -------------------------------------------------------------------------------- /src/stories/MapAnnotations/Popup/Basic/Popup.tsx: -------------------------------------------------------------------------------- 1 | import { AzureMap, AzureMapsProvider, AzureMapPopup, IAzureMapPopup } from 'react-azure-maps'; 2 | import { mapOptions } from '../../../../key'; 3 | 4 | const Popup = ({ isVisible, options }: IAzureMapPopup) => { 5 | // use position as argument would be better 6 | return ( 7 | 8 |
9 | 10 | Hello World
} 14 | /> 15 |
16 | 17 | 18 | ); 19 | }; 20 | 21 | export default Popup; 22 | -------------------------------------------------------------------------------- /src/stories/MapAnnotations/Popup/Interactive/InteractivePopup.mdx: -------------------------------------------------------------------------------- 1 | import { Meta, Source } from '@storybook/blocks'; 2 | 3 | import * as InteractivePopupStories from './InteractivePopup.stories'; 4 | 5 | import InteractivePopup from './InteractivePopupExample'; 6 | 7 | 8 | 9 | # Interactive Popup 10 | Besides the static popup, you can also create an interactive popup containing React components.
11 | Therefore, you can easily change the states of the components both inside and outside the popup.
12 | ## Example 13 | Here is an example of an interactive popup that shows a counter counting the number of times the user has clicked on the popup.
14 | You can also change the popup's color by clicking the button on the top left corner. 15 | 16 | 17 | 18 | 19 | Let's take a look of how the interactive popup is implemented.
20 | ## Implementation 21 | ### 1. Create an interactive popup component 22 | Here we initialize a new Popup instance and render the React node children for the popup's content.
23 | 24 | { 38 | const { mapRef } = useContext(AzureMapsContext); 39 | const containerRef = document.createElement('div'); 40 | const root = createRoot(containerRef); 41 | const [popupRef] = useState(new atlas.Popup({ ...options, content: containerRef })); 42 | 43 | // Add events to the popup when it is mounted 44 | useEffect(() => { 45 | if (mapRef) { 46 | events && 47 | events.forEach(({ eventName, callback }) => { 48 | mapRef.events.add(eventName, popupRef, callback); 49 | }); 50 | return () => { 51 | mapRef.popups.remove(popupRef); 52 | }; 53 | } 54 | }, []); 55 | 56 | // Render the popup content and set the options 57 | useEffect(() => { 58 | root.render(children); 59 | popupRef.setOptions({ 60 | ...options, 61 | content: containerRef, 62 | }); 63 | if (mapRef && isVisible && !popupRef.isOpen()) { 64 | popupRef.open(mapRef); 65 | } 66 | }, [options, children]); 67 | 68 | // Toggle the popup visibility 69 | useEffect(() => { 70 | if (mapRef) { 71 | if (isVisible && !popupRef.isOpen()) { 72 | popupRef.open(mapRef); 73 | } else if (mapRef.popups.getPopups().length && !isVisible && popupRef.isOpen()) { 74 | popupRef.close(); 75 | } 76 | } 77 | }, [isVisible]); 78 | 79 | return null; 80 | }; 81 | 82 | export default InteractivePopup; 83 | 84 | `} /> 85 | 86 | ### 2. Create your popup content 87 | In this example we create a simple counter component that increments the count when the user clicks on the popup.
88 | Also, it accepts a background color as a prop to change the popup's color.
89 | **You can create any kind of React component as the popup content.** 90 | 91 | { 95 | const [count, setCount] = useState(0); 96 | 97 | return ( 98 |
99 |

This is a counter:

100 |

You have clicked {count} times.

101 | 114 | 128 |
129 | ); 130 | }; 131 | 132 | export default PopupContent; 133 | `}/> 134 | 135 | ### 3. Use the interactive popup 136 | Finally, you can use the interactive popup component on your map and pass your react component as children.
137 | 138 | { 145 | const [bgColor, setBgColor] = useState('white'); 146 | 147 | // click to change color randomly 148 | const changeColor = () => { 149 | const color = \`#\${Math.floor(Math.random() * 16777215).toString(16)}\`; 150 | setBgColor(color); 151 | }; 152 | return ( 153 | 154 |
155 | 158 |
159 | 160 | 161 | 162 | 163 | 164 |
165 |
166 |
167 | ); 168 | }; 169 | `} /> 170 | -------------------------------------------------------------------------------- /src/stories/MapAnnotations/Popup/Interactive/InteractivePopup.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react'; 2 | import InteractivePopup from './InteractivePopupExample'; 3 | 4 | const meta: Meta = { 5 | title: 'Map Annotations/Popup/Interactive Popup', 6 | component: InteractivePopup, 7 | args: { 8 | isVisible: true, 9 | options: { 10 | position: [0, 0], 11 | }, 12 | }, 13 | parameters: { 14 | storySource: { 15 | source: ` 16 | import { useContext, useEffect, useState, ReactNode } from 'react'; 17 | import { createRoot } from 'react-dom/client'; 18 | import atlas from 'azure-maps-control'; 19 | import { IAzureMapsContextProps, AzureMapsContext, IAzureMapPopupEvent } from 'react-azure-maps'; 20 | 21 | interface InteractivePopupProps { 22 | children: ReactNode; 23 | isVisible?: boolean; 24 | options?: atlas.PopupOptions; 25 | events?: IAzureMapPopupEvent[]; 26 | }; 27 | 28 | const InteractivePopup = ({ children, isVisible = true, options, events }: InteractivePopupProps) => { 29 | const { mapRef } = useContext(AzureMapsContext); 30 | const containerRef = document.createElement('div'); 31 | const root = createRoot(containerRef); 32 | const [popupRef] = useState(new atlas.Popup({ ...options, content: containerRef })); 33 | 34 | // Add events to the popup when it is mounted 35 | useEffect(() => { 36 | if (mapRef) { 37 | events && 38 | events.forEach(({ eventName, callback }) => { 39 | mapRef.events.add(eventName, popupRef, callback); 40 | }); 41 | return () => { 42 | mapRef.popups.remove(popupRef); 43 | }; 44 | } 45 | }, []); 46 | 47 | // Render the popup content and set the options 48 | useEffect(() => { 49 | root.render(children); 50 | popupRef.setOptions({ 51 | ...options, 52 | content: containerRef, 53 | }); 54 | if (mapRef && isVisible && !popupRef.isOpen()) { 55 | popupRef.open(mapRef); 56 | } 57 | }, [options, children]); 58 | 59 | // Toggle the popup visibility 60 | useEffect(() => { 61 | if (mapRef) { 62 | if (isVisible && !popupRef.isOpen()) { 63 | popupRef.open(mapRef); 64 | } else if (mapRef.popups.getPopups().length && !isVisible && popupRef.isOpen()) { 65 | popupRef.close(); 66 | } 67 | } 68 | }, [isVisible]); 69 | 70 | return null; 71 | }; 72 | `, 73 | }, 74 | }, 75 | }; 76 | 77 | export default meta; 78 | 79 | type Story = StoryObj; 80 | 81 | export const Example: Story = {}; 82 | -------------------------------------------------------------------------------- /src/stories/MapAnnotations/Popup/Interactive/InteractivePopup.tsx: -------------------------------------------------------------------------------- 1 | import { useContext, useEffect, useState, ReactNode } from 'react'; 2 | import { createRoot } from 'react-dom/client'; 3 | import atlas from 'azure-maps-control'; 4 | import { IAzureMapsContextProps, AzureMapsContext, IAzureMapPopupEvent } from 'react-azure-maps'; 5 | 6 | interface InteractivePopupProps { 7 | children: ReactNode; 8 | isVisible?: boolean; 9 | options?: atlas.PopupOptions; 10 | events?: IAzureMapPopupEvent[]; 11 | } 12 | 13 | const InteractivePopup = ({ children, isVisible = true, options, events }: InteractivePopupProps) => { 14 | const { mapRef } = useContext(AzureMapsContext); 15 | const containerRef = document.createElement('div'); 16 | const root = createRoot(containerRef); 17 | const [popupRef] = useState(new atlas.Popup({ ...options, content: containerRef })); 18 | 19 | // Add events to the popup when it is mounted 20 | useEffect(() => { 21 | if (mapRef) { 22 | events && 23 | events.forEach(({ eventName, callback }) => { 24 | mapRef.events.add(eventName, popupRef, callback); 25 | }); 26 | return () => { 27 | mapRef.popups.remove(popupRef); 28 | }; 29 | } 30 | }, []); 31 | 32 | // Render the popup content and set the options 33 | useEffect(() => { 34 | root.render(children); 35 | popupRef.setOptions({ 36 | ...options, 37 | content: containerRef, 38 | }); 39 | if (mapRef && isVisible && !popupRef.isOpen()) { 40 | popupRef.open(mapRef); 41 | } 42 | }, [options, children]); 43 | 44 | // Toggle the popup visibility 45 | useEffect(() => { 46 | if (mapRef) { 47 | if (isVisible && !popupRef.isOpen()) { 48 | popupRef.open(mapRef); 49 | } else if (mapRef.popups.getPopups().length && !isVisible && popupRef.isOpen()) { 50 | popupRef.close(); 51 | } 52 | } 53 | }, [isVisible]); 54 | 55 | return null; 56 | }; 57 | 58 | export default InteractivePopup; 59 | -------------------------------------------------------------------------------- /src/stories/MapAnnotations/Popup/Interactive/InteractivePopupExample.tsx: -------------------------------------------------------------------------------- 1 | import { AzureMap, AzureMapsProvider, IAzureMapPopup } from 'react-azure-maps'; 2 | import { mapOptions } from '../../../../key'; 3 | import InteractivePopup from './InteractivePopup'; 4 | import PopupContent from './PopupContent'; 5 | import { useState } from 'react'; 6 | 7 | const InteractivePopupExample = ({ isVisible, options }: IAzureMapPopup) => { 8 | const [bgColor, setBgColor] = useState('white'); 9 | 10 | // click to change color randomly 11 | const changeColor = () => { 12 | const color = `#${Math.floor(Math.random() * 16777215).toString(16)}`; 13 | setBgColor(color); 14 | }; 15 | return ( 16 | 17 |
18 | 21 |
22 | 23 | 24 | 25 | 26 | 27 |
28 |
29 |
30 | ); 31 | }; 32 | 33 | export default InteractivePopupExample; 34 | -------------------------------------------------------------------------------- /src/stories/MapAnnotations/Popup/Interactive/PopupContent.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | 3 | const PopupContent = ({ bgColor }: { bgColor: string }) => { 4 | const [count, setCount] = useState(0); 5 | 6 | return ( 7 |
8 |

This is a counter:

9 |

You have clicked {count} times.

10 | 23 | 37 |
38 | ); 39 | }; 40 | 41 | export default PopupContent; 42 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "esModuleInterop": true, 8 | "allowSyntheticDefaultImports": true, 9 | "strict": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "noEmit": true, 16 | "jsx": "react-jsx", 17 | "noFallthroughCasesInSwitch": true 18 | }, 19 | "include": ["src"] 20 | } 21 | --------------------------------------------------------------------------------