├── .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 | You need to enable JavaScript to run this app.
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 | setMapCenter(getRandomPosition())}>
76 | Change Map Center
77 |
78 |
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 | setShowTileBoundaries(!showTileBoundaries)}
119 | >
120 | Toggle Tile Boundaries
121 |
122 |
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 | setMapCenter(getRandomPosition())}>
19 | Change Map Center
20 |
21 |
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 | setShowTileBoundaries(!showTileBoundaries)}
25 | >
26 | Toggle Tile Boundaries
27 |
28 |
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 |
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 |
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 |
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 |
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 | }
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 |
{
109 | setCount(count + 1);
110 | }}
111 | >
112 | Click me
113 |
114 |
{
123 | setCount(0);
124 | }}
125 | >
126 | Reset
127 |
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 |
156 | Change popup color
157 |
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 |
19 | Change popup color
20 |
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 |
{
18 | setCount(count + 1);
19 | }}
20 | >
21 | Click me
22 |
23 |
{
32 | setCount(0);
33 | }}
34 | >
35 | Reset
36 |
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 |
--------------------------------------------------------------------------------