UNPKG

@pnp/spfx-controls-react

Version:

Reusable React controls for SharePoint Framework solutions

417 lines (345 loc) 10.6 kB
# WorldMapControl Documentation A React component for displaying interactive world maps with markers, search functionality, and customizable styling using MapLibre GL JS. ## Table of Contents - [Installation](#installation) - [Basic Usage](#basic-usage) - [Properties](#properties) - [Data Structure](#data-structure) - [Map Configuration](#map-configuration) - [Search Feature](#search-feature) - [Marker Customization](#marker-customization) - [Examples](#examples) ## Installation ```bash npm install @pnp/spfx-controls-react ``` ## Basic Usage ```tsx import React from 'react'; import { MaplibreWorldMap } from '@pnp/spfx-controls-react/lib/WorldMapControl'; import { IData } from '@pnp/spfx-controls-react/lib/worldMap'; const MyMapComponent: React.FC = () => { const locations: IData[] = [ { id: '1', name: 'New York', imageUrl: 'https://example.com/nyc.jpg', link: 'https://example.com/nyc', coordinates: [-74.006, 40.7128] }, { id: '2', name: 'London', imageUrl: 'https://example.com/london.jpg', link: 'https://example.com/london', coordinates: [-0.1276, 51.5074] } ]; const handleMarkerClick = (location: IData) => { console.log('Clicked:', location.name); // Navigate to location.link or show details }; return ( <MaplibreWorldMap data={locations} onClick={handleMarkerClick} title="My World Map" /> ); }; ``` ## Properties ### Core Properties | Property | Type | Required | Default | Description | |----------|------|----------|---------|-------------| | `data` | `IData[]` | ✅ | - | Array of location data to display | | `onClick` | `(item: IData) => void` | ❌ | - | Callback when marker is clicked | | `title` | `string \| ReactNode` | ❌ | `'World Map'` | Map title | | `style` | `CSSProperties` | ❌ | - | Custom styles for map container | | `className` | `string` | ❌ | - | CSS class for root container | | `theme` | `Theme` | ❌ | - | Fluent UI theme object | ### Map Configuration | Property | Type | Required | Default | Description | |----------|------|----------|---------|-------------| | `mapKey` | `string` | ❌ | - | MapTiler API key | | `mapStyleUrl` | `string` | ❌ | - | Custom map style URL | | `fitPadding` | `number` | ❌ | `20` | Padding when fitting to markers | ### Search Configuration | Property | Type | Required | Default | Description | |----------|------|----------|---------|-------------| | `search.enabled` | `boolean` | ❌ | `true` | Enable search feature | | `search.placeholder` | `string` | ❌ | `'Search locations...'` | Search input placeholder | | `search.searchField` | `keyof IData \| function` | ❌ | `'name'` | Field to search or custom function | | `search.zoomLevel` | `number` | ❌ | `8` | Zoom level for search results | | `search.position` | `object` | ❌ | `{top: '10px', left: '10px'}` | Search overlay position | | `search.onSearchChange` | `function` | ❌ | - | Callback when search changes | ### Marker Configuration | Property | Type | Required | Default | Description | |----------|------|----------|---------|-------------| | `marker.markerClassName` | `string` | ❌ | - | CSS class for markers | | `marker.markerStyle` | `CSSProperties` | ❌ | - | Custom marker styles | | `marker.imageSize` | `number` | ❌ | `40` | Marker image size (px) | | `marker.renderToolTip` | `function` | ❌ | - | Custom tooltip renderer | | `marker.tooltipClassName` | `string` | ❌ | - | CSS class for tooltips | | `marker.tooltipStyle` | `CSSProperties` | ❌ | - | Custom tooltip styles | ## Data Structure Each location item must implement the `IData` interface: ```tsx interface IData { id: string; // Unique identifier name: string; // Display name (used for search) imageUrl: string; // Image URL for marker/tooltip link: string; // Associated URL/link coordinates: [number, number]; // [longitude, latitude] } ``` ### Example Data ```tsx const sampleData: IData[] = [ { id: 'paris-001', name: 'Paris, France', imageUrl: 'https://example.com/images/paris.jpg', link: 'https://example.com/locations/paris', coordinates: [2.3522, 48.8566] // [longitude, latitude] }, { id: 'tokyo-001', name: 'Tokyo, Japan', imageUrl: 'https://example.com/images/tokyo.jpg', link: '/locations/tokyo', coordinates: [139.6917, 35.6895] } ]; ``` ## Map Configuration ### Using Demo Map (Free) ```tsx <MaplibreWorldMap data={locations} /> ``` ### Using MapTiler API Key ```tsx <MaplibreWorldMap data={locations} mapKey="your-maptiler-api-key" /> ``` ### Custom Map Style ```tsx <MaplibreWorldMap data={locations} mapKey="your-api-key" mapStyleUrl="https://api.maptiler.com/maps/satellite/style.json" /> ``` ### Map Style Priority 1. **mapKey + mapStyleUrl**: Uses the style URL with API key 2. **mapKey only**: Uses MapTiler streets style 3. **mapStyleUrl only**: Uses URL as-is 4. **Neither**: Uses free demo map ## Search Feature ### Basic Search ```tsx <MaplibreWorldMap data={locations} search={{ enabled: true, placeholder: "Find a location..." }} /> ``` ### Custom Search Field ```tsx <MaplibreWorldMap data={locations} search={{ searchField: 'id', // Search by ID instead of name }} /> ``` ### Custom Search Function ```tsx <MaplibreWorldMap data={locations} search={{ searchField: (item) => `${item.name} ${item.link}`, // Search multiple fields onSearchChange: (term, results) => { console.log(`Found ${results.length} results for "${term}"`); } }} /> ``` ### Search Positioning ```tsx <MaplibreWorldMap data={locations} search={{ position: { top: '10px', right: '10px' // Top-right corner } }} /> ``` ## Marker Customization ### Basic Marker Styling ```tsx <MaplibreWorldMap data={locations} marker={{ imageSize: 50, markerStyle: { borderRadius: '50%', border: '2px solid #0078d4' } }} /> ``` ### Custom Tooltips ```tsx <MaplibreWorldMap data={locations} marker={{ renderToolTip: (item) => ( <div> <h4>{item.name}</h4> <img src={item.imageUrl} alt={item.name} style={{width: '100px'}} /> <p>Click to visit: <a href={item.link}>Learn more</a></p> </div> ), tooltipStyle: { backgroundColor: 'white', border: '1px solid #ccc', borderRadius: '8px', padding: '12px', maxWidth: '200px' } }} /> ``` ## Examples ### Complete Example with All Features ```tsx import React from 'react'; import { MaplibreWorldMap } from '@pnp/spfx-controls-react/lib/WorldMapControl'; import { IData } from '@pnp/spfx-controls-react/lib/worldMap'; const AdvancedMapExample: React.FC = () => { const locations: IData[] = [ { id: 'nyc', name: 'New York City', imageUrl: 'https://example.com/nyc.jpg', link: 'https://example.com/nyc', coordinates: [-74.006, 40.7128] }, // ... more locations ]; const handleLocationClick = (location: IData) => { window.open(location.link, '_blank'); }; const handleSearchChange = (term: string, results: IData[]) => { console.log(`Search: "${term}" - ${results.length} results`); }; return ( <MaplibreWorldMap data={locations} onClick={handleLocationClick} mapKey="your-maptiler-api-key" title="Global Office Locations" style={{ width: '100%', height: '600px', border: '1px solid #e1e1e1', borderRadius: '8px' }} fitPadding={50} search={{ enabled: true, placeholder: "Search office locations...", zoomLevel: 10, position: { top: '15px', right: '15px' }, onSearchChange: handleSearchChange, searchField: (item) => `${item.name} ${item.id}` }} marker={{ imageSize: 45, markerStyle: { borderRadius: '50%', border: '3px solid #0078d4', boxShadow: '0 2px 8px rgba(0,0,0,0.3)' }, renderToolTip: (item) => ( <div> <h4 style={{margin: '0 0 8px 0'}}>{item.name}</h4> <img src={item.imageUrl} alt={item.name} style={{width: '120px', borderRadius: '4px'}} /> <p style={{margin: '8px 0 0 0', fontSize: '12px'}}> Click marker to visit location page </p> </div> ), tooltipStyle: { backgroundColor: 'white', border: '1px solid #ccc', borderRadius: '8px', padding: '12px', maxWidth: '160px', textAlign: 'center' } }} /> ); }; export default AdvancedMapExample; ``` ### Minimal Example ```tsx <MaplibreWorldMap data={[ { id: '1', name: 'Paris', imageUrl: 'paris.jpg', link: '/paris', coordinates: [2.3522, 48.8566] } ]} onClick={(location) => alert(`Clicked: ${location.name}`)} /> ``` ## TypeScript Support The component is fully typed with TypeScript. Import the interfaces for type safety: ```tsx import { IMaplibreWorldMapProps, IData } from '@pnp/spfx-controls-react/lib/worldMap'; const MyComponent: React.FC<{locations: IData[]}> = ({ locations }) => { const mapProps: IMaplibreWorldMapProps = { data: locations, onClick: (item) => console.log(item.name), // ... other props with full type checking }; return <MaplibreWorldMap {...mapProps} />; }; ``` ## Browser Support - Modern browsers with ES6+ support - WebGL support required for map rendering - Mobile browsers supported ## Performance Tips 1. **Limit data size**: For large datasets (>100 markers), consider clustering or pagination 2. **Optimize images**: Use appropriate image sizes for markers 3. **API key**: Use your own MapTiler API key for production to avoid rate limits 4. **Lazy loading**: Load the component only when needed to reduce initial bundle size ## Troubleshooting ### Common Issues 1. **Map not loading**: Check if `mapKey` is valid or use demo map 2. **Markers not appearing**: Verify `coordinates` format is `[longitude, latitude]` 3. **Search not working**: Ensure `searchField` matches available data properties 4. **Performance issues**: Reduce number of markers or optimize marker images ### Getting Help - Check the browser console for error messages - Verify all required properties are provided - Ensure coordinate format is correct (longitude first, then latitude) - Test with demo data to isolate issues