mehdi-akbari-map 1.3.1 → 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/react.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/InteractiveMap.tsx","../src/components/Map.tsx","../src/utils/createCustomMarkerElement.ts","../src/utils/createClusterElement.ts","../src/components/StoreCard.tsx"],"sourcesContent":["\"use client\";\r\n\r\nimport React, { useState, useEffect, useRef, useCallback } from 'react';\r\nimport Map from './Map';\r\nimport { StoreCard } from './StoreCard';\r\nimport { Store, MapProps } from '../types';\r\n\r\nexport const InteractiveMap: React.FC<MapProps> = (props) => {\r\n const [isMobile, setIsMobile] = useState(false);\r\n const [mapInstance, setMapInstance] = useState<any>(null);\r\n const sliderRef = useRef<HTMLDivElement>(null);\r\n\r\n useEffect(() => {\r\n const checkMobile = () => setIsMobile(window.innerWidth < 1024);\r\n checkMobile();\r\n window.addEventListener('resize', checkMobile);\r\n return () => window.removeEventListener('resize', checkMobile);\r\n }, []);\r\n\r\n useEffect(() => {\r\n if (isMobile && props.selectedMarkerId && sliderRef.current) {\r\n const timer = setTimeout(() => {\r\n const selectedCard = sliderRef.current?.querySelector(`[data-id=\"${props.selectedMarkerId}\"]`);\r\n if (selectedCard) {\r\n selectedCard.scrollIntoView({ behavior: 'smooth', inline: 'center', block: 'nearest' });\r\n }\r\n }, 150);\r\n return () => clearTimeout(timer);\r\n }\r\n }, [props.selectedMarkerId, isMobile]);\r\n\r\n const handleStoreSelect = useCallback((store: Store, index: number) => {\r\n if (mapInstance) {\r\n props.onMarkerClick?.(store, index, mapInstance);\r\n }\r\n }, [mapInstance, props]);\r\n\r\n const safeMarkers = props.markers || [];\r\n\r\n return (\r\n <div className={`map-layout-root ${isMobile ? 'is-mobile' : 'is-desktop'}`}>\r\n {!isMobile && (\r\n <aside className=\"sidebar\">\r\n <header className=\"sidebar-header\">لیست فروشگاه‌ها</header>\r\n <div className=\"sidebar-list\">\r\n {safeMarkers.map((store, idx) => (\r\n <StoreCard \r\n key={store.id} \r\n store={store} \r\n isSelected={props.selectedMarkerId === store.id}\r\n onSelect={() => handleStoreSelect(store, idx)}\r\n shopLogoUrl={store.logoUrl}\r\n />\r\n ))}\r\n </div>\r\n </aside>\r\n )}\r\n\r\n <main className=\"map-container\">\r\n <Map \r\n {...props} \r\n onMapLoad={(map) => {\r\n if (map) {\r\n setMapInstance(map);\r\n props.onMapLoad?.(map);\r\n }\r\n }} \r\n />\r\n \r\n {props.productName && (\r\n <div className=\"product-badge\">\r\n <div className=\"product-badge-left-side\">\r\n {props.productLogoUrl && (\r\n <img src={props.productLogoUrl} alt=\"product\" className=\"product-badge-logo\" />\r\n )}\r\n <span className=\"product-badge-fixed-label\">فروشگاه‌های حضوری</span>\r\n </div>\r\n <div className=\"product-badge-separator\"></div>\r\n <span className=\"product-badge-text\">{props.productName}</span>\r\n </div>\r\n )}\r\n\r\n {isMobile && (\r\n <div className=\"mobile-slider\" ref={sliderRef}>\r\n {safeMarkers.map((store, idx) => (\r\n <StoreCard \r\n key={store.id} \r\n store={store} \r\n isSelected={props.selectedMarkerId === store.id}\r\n onSelect={() => handleStoreSelect(store, idx)}\r\n shopLogoUrl={store.logoUrl}\r\n />\r\n ))}\r\n </div>\r\n )}\r\n </main>\r\n </div>\r\n );\r\n};","\"use client\";\r\n\r\nimport React, { useEffect, useRef, useState, useCallback } from 'react';\r\nimport type { MapboxMap, Store, MapProps } from '../types';\r\nimport { createCustomMarkerElement } from '../utils/createCustomMarkerElement';\r\nimport { createClusterElement } from '../utils/createClusterElement';\r\nimport Supercluster from 'supercluster';\r\n\r\n\r\nconst Map: React.FC<MapProps> = ({\r\n options,\r\n markers = [],\r\n markerLogoUrl = '',\r\n selectedMarkerId = null,\r\n onMarkerClick,\r\n onMapLoad,\r\n style = { width: '100%', height: '100%' },\r\n}) => {\r\n const mapContainerRef = useRef<HTMLDivElement>(null);\r\n const [mapInstance, setMapInstance] = useState<MapboxMap | null>(null);\r\n const [mapLib, setMapLib] = useState<any>(null);\r\n const markersRef = useRef<any[]>([]);\r\n const isRemoving = useRef(false); \r\n \r\n const clusterIndex = useRef(new Supercluster({\r\n radius: 60,\r\n maxZoom: 16\r\n }));\r\n\r\n \r\n useEffect(() => {\r\n let mounted = true;\r\n import('@neshan-maps-platform/mapbox-gl').then((mod) => {\r\n if (mounted) setMapLib(mod.default || mod);\r\n });\r\n return () => { mounted = false; };\r\n }, []);\r\n\r\n \r\n useEffect(() => {\r\n if (!mapLib || !mapContainerRef.current || mapInstance) return;\r\n\r\n const map = new mapLib.Map({\r\n container: mapContainerRef.current,\r\n ...options,\r\n });\r\n\r\n map.on('load', () => {\r\n if (!isRemoving.current) {\r\n setMapInstance(map);\r\n onMapLoad?.(map);\r\n }\r\n });\r\n\r\n return () => {\r\n if (map) {\r\n isRemoving.current = true;\r\n \r\n setTimeout(() => {\r\n try {\r\n map.remove();\r\n } catch (e) {\r\n console.warn(\"Mapbox remove error ignored:\", e);\r\n }\r\n }, 0);\r\n }\r\n };\r\n \r\n }, [mapLib]); \r\n\r\n \r\n useEffect(() => {\r\n if (!mapInstance || !selectedMarkerId || isRemoving.current) return;\r\n const selectedStore = markers.find(m => m.id === selectedMarkerId);\r\n if (selectedStore) {\r\n mapInstance.flyTo({\r\n center: [selectedStore.lng, selectedStore.lat],\r\n zoom: 16, \r\n essential: true,\r\n duration: 1200\r\n });\r\n }\r\n }, [selectedMarkerId, mapInstance, markers]);\r\n\r\n \r\n const updateMarkers = useCallback(() => {\r\n if (!mapInstance || !mapLib || isRemoving.current) return;\r\n\r\n try {\r\n const bounds = mapInstance.getBounds();\r\n const zoom = Math.floor(mapInstance.getZoom());\r\n\r\n const clusters = clusterIndex.current.getClusters(\r\n [bounds.getWest(), bounds.getSouth(), bounds.getEast(), bounds.getNorth()],\r\n zoom\r\n );\r\n\r\n markersRef.current.forEach(m => m.remove());\r\n markersRef.current = [];\r\n\r\n clusters.forEach(cluster => {\r\n const [lng, lat] = cluster.geometry.coordinates;\r\n const { cluster: isCluster, point_count: count } = cluster.properties;\r\n\r\n let el: HTMLElement;\r\n\r\n if (isCluster) {\r\n el = createClusterElement(count);\r\n el.onclick = (e) => {\r\n e.stopPropagation();\r\n const expansionZoom = Math.min(\r\n clusterIndex.current.getClusterExpansionZoom(cluster.id as number),\r\n 18\r\n );\r\n mapInstance.easeTo({ center: [lng, lat], zoom: expansionZoom });\r\n };\r\n } else {\r\n const storeData = cluster.properties as Store;\r\n el = createCustomMarkerElement({\r\n marker: storeData,\r\n isSelected: storeData.id === selectedMarkerId,\r\n logoSrc: markerLogoUrl,\r\n });\r\n el.onclick = () => onMarkerClick?.(storeData, 0, mapInstance);\r\n }\r\n\r\n const newMarker = new mapLib.Marker({ element: el, anchor: isCluster ? 'center' : 'bottom' })\r\n .setLngLat([lng, lat])\r\n .addTo(mapInstance);\r\n \r\n markersRef.current.push(newMarker);\r\n });\r\n } catch (err) {\r\n console.error(\"Update markers failed:\", err);\r\n }\r\n }, [mapInstance, mapLib, selectedMarkerId, markerLogoUrl, onMarkerClick]);\r\n\r\n \r\n useEffect(() => {\r\n if (isRemoving.current) return;\r\n const points: any = markers.map(s => ({\r\n type: 'Feature',\r\n properties: { ...s },\r\n geometry: { type: 'Point', coordinates: [s.lng, s.lat] }\r\n }));\r\n clusterIndex.current.load(points);\r\n updateMarkers();\r\n }, [markers, updateMarkers]);\r\n\r\n \r\n useEffect(() => {\r\n if (!mapInstance || isRemoving.current) return;\r\n mapInstance.on('moveend', updateMarkers);\r\n return () => { mapInstance.off('moveend', updateMarkers); };\r\n }, [mapInstance, updateMarkers]);\r\n\r\n return (\r\n <div style={{ position: 'relative', overflow: 'hidden', ...style }}>\r\n <div \r\n ref={mapContainerRef} \r\n style={{ width: '100%', height: '100%', backfaceVisibility: 'hidden' }} \r\n />\r\n </div>\r\n );\r\n};\r\n\r\nexport default Map;","import type { Store } from '../types';\r\n\r\ninterface CreateMarkerOptions {\r\n marker: Store;\r\n isSelected: boolean;\r\n logoSrc?: string;\r\n}\r\n\r\nexport function createCustomMarkerElement({\r\n marker,\r\n isSelected,\r\n logoSrc = '',\r\n}: CreateMarkerOptions): HTMLElement {\r\n const container = document.createElement('div');\r\n container.className = `neshan-marker-container ${isSelected ? 'neshan-marker-selected' : ''}`;\r\n\r\n const body = document.createElement('div');\r\n body.className = 'neshan-marker-body';\r\n\r\n const iconBox = document.createElement('div');\r\n Object.assign(iconBox.style, {\r\n width: '40px',\r\n height: '40px',\r\n borderRadius: '50%',\r\n backgroundColor: 'white',\r\n border: isSelected ? '3px solid #2563eb' : '',\r\n boxShadow: '0 4px 8px rgba(0,0,0,0.2)',\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n overflow: 'hidden'\r\n });\r\n\r\n if (logoSrc) {\r\n const img = document.createElement('img');\r\n img.src = logoSrc;\r\n img.style.width = '100%';\r\n img.style.height = '100%';\r\n img.style.objectFit = 'cover';\r\n iconBox.appendChild(img);\r\n } else {\r\n iconBox.style.backgroundColor = isSelected ? '#2563eb' : '#ef4444';\r\n }\r\n\r\n body.appendChild(iconBox);\r\n\r\n \r\n if (marker.name || marker.price) {\r\n const label = document.createElement('div');\r\n label.className = 'neshan-marker-label';\r\n \r\n const pricePart = marker.price ? ` (${marker.price} ت)` : '';\r\n label.textContent = `${marker.name}${pricePart}`;\r\n \r\n body.appendChild(label);\r\n }\r\n\r\n container.appendChild(body);\r\n return container;\r\n}","export function createClusterElement(count: number): HTMLElement {\r\n const container = document.createElement('div');\r\n container.className = 'neshan-cluster-marker';\r\n \r\n const countSpan = document.createElement('span');\r\n countSpan.className = 'neshan-cluster-count';\r\n countSpan.innerText = count.toString();\r\n \r\n container.appendChild(countSpan);\r\n return container;\r\n}","import React from 'react';\r\nimport { Store } from '../types';\r\n\r\ninterface StoreCardProps {\r\n store: Store;\r\n isSelected: boolean;\r\n onSelect: () => void;\r\n shopLogoUrl?: string; // لوگوی کوچک کنار توضیحات\r\n}\r\n\r\nexport const StoreCard: React.FC<StoreCardProps> = ({ store, isSelected, onSelect, shopLogoUrl }) => {\r\n const googleMapUrl = `https://www.google.com/maps/dir/?api=1&destination=${store.lat},${store.lng}`;\r\n\r\n const handleActionClick = (e: React.MouseEvent) => {\r\n e.stopPropagation();\r\n };\r\n\r\n return (\r\n <div \r\n className={`store-card ${isSelected ? 'active' : ''}`}\r\n onClick={onSelect}\r\n data-id={store.id}\r\n >\r\n {/* ردیف اول: نام، شهر و گزارش */}\r\n <div className=\"store-card-header\">\r\n <div className=\"store-info-main\">\r\n <h4 className=\"store-name\">{store.name}</h4>\r\n <div className=\"store-location-tag\">\r\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\"><path d=\"M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z\"></path><circle cx=\"12\" cy=\"10\" r=\"3\"></circle></svg>\r\n <span className=\"store-city\">{store.city}</span>\r\n </div>\r\n </div>\r\n <button className=\"report-btn\" onClick={handleActionClick}>\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#6b7280\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\"><path d=\"M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z\"></path><line x1=\"4\" y1=\"22\" x2=\"4\" y2=\"15\"></line></svg>\r\n <span>گزارش</span>\r\n </button>\r\n </div>\r\n\r\n {/* بخش عملکرد (اختیاری - اگر در دیتا بود) */}\r\n {store.performance && (\r\n <div className=\"store-performance\">\r\n عملکرد <span className=\"perf-value\">{store.performance}</span>\r\n </div>\r\n )}\r\n\r\n {/* بخش توضیحات همراه با لوگو */}\r\n <div className=\"store-desc-section\">\r\n {shopLogoUrl && (\r\n <img src={shopLogoUrl} alt=\"shop logo\" className=\"store-mini-logo\" />\r\n )}\r\n <p className=\"store-desc\">{store.desc}</p>\r\n </div>\r\n\r\n {/* ردیف آخر: قیمت و اطلاعات تماس */}\r\n <div className=\"store-card-footer\">\r\n <div className=\"price-container\">\r\n <span className=\"price-value\">{store.price}</span>\r\n <span className=\"price-unit\">تومان</span>\r\n </div>\r\n <div className=\"contact-trigger\">\r\n <span>اطلاعات تماس</span>\r\n <svg className={`arrow-icon ${isSelected ? 'open' : ''}`} width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\"><polyline points=\"18 15 12 9 6 15\"></polyline></svg>\r\n </div>\r\n </div>\r\n\r\n {/* بخش دکمه‌های عملیاتی (عمودی) */}\r\n <div className=\"store-details-wrapper\">\r\n <div className=\"store-details-content\">\r\n <div className=\"action-buttons vertical\">\r\n {store.phone && (\r\n <a href={`tel:${store.phone}`} className=\"btn btn-call full-width\" onClick={handleActionClick}>\r\n <div className=\"btn-content\">\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\"><path d=\"M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z\"></path></svg>\r\n <span>تماس: {store.phone}</span>\r\n </div>\r\n </a>\r\n )}\r\n {store.whatsapp && (\r\n <a href={`https://wa.me/${store.whatsapp}`} target=\"_blank\" rel=\"noreferrer\" className=\"btn btn-wa full-width\" onClick={handleActionClick}>\r\n <div className=\"btn-content\">\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\"><path d=\"M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z\"></path></svg>\r\n <span>ارسال پیام در واتساپ</span>\r\n </div>\r\n </a>\r\n )}\r\n <a href={googleMapUrl} target=\"_blank\" rel=\"noreferrer\" className=\"btn btn-route full-width\" onClick={handleActionClick}>\r\n <div className=\"btn-content\">\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\"><polygon points=\"3 11 22 2 13 21 11 13 3 11\"></polygon></svg>\r\n <span>مسیریابی روی نقشه</span>\r\n </div>\r\n </a>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n};"],"mappings":"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","names":["useState","useEffect","useRef","useCallback","jsx","jsx","jsxs","useState","useRef","useEffect","useCallback"]}
1
+ {"version":3,"sources":["../src/components/InteractiveMap.tsx","../src/components/Map.tsx","../src/utils/createCustomMarkerElement.ts","../src/utils/createClusterElement.ts","../src/components/StoreCard.tsx"],"sourcesContent":["\"use client\";\r\n\r\nimport React, { useState, useEffect, useRef, useCallback } from 'react';\r\nimport Map from './Map';\r\nimport { StoreCard } from './StoreCard';\r\nimport { Store, MapProps } from '../types';\r\n\r\nexport const InteractiveMap: React.FC<MapProps> = (props) => {\r\n const [isMobile, setIsMobile] = useState(false);\r\n const [mapInstance, setMapInstance] = useState<any>(null);\r\n const sliderRef = useRef<HTMLDivElement>(null);\r\n\r\n useEffect(() => {\r\n const checkMobile = () => setIsMobile(window.innerWidth < 1024);\r\n checkMobile();\r\n window.addEventListener('resize', checkMobile);\r\n return () => window.removeEventListener('resize', checkMobile);\r\n }, []);\r\n\r\n // هماهنگی اسکرول\r\n useEffect(() => {\r\n if (isMobile && props.selectedMarkerId && sliderRef.current) {\r\n const timer = setTimeout(() => {\r\n const selectedCard = sliderRef.current?.querySelector(`[data-id=\"${props.selectedMarkerId}\"]`);\r\n if (selectedCard) {\r\n selectedCard.scrollIntoView({ behavior: 'smooth', inline: 'center', block: 'nearest' });\r\n }\r\n }, 150);\r\n return () => clearTimeout(timer);\r\n }\r\n }, [props.selectedMarkerId, isMobile]);\r\n\r\n const handleStoreSelect = useCallback((store: Store, index: number) => {\r\n if (mapInstance) {\r\n props.onMarkerClick?.(store, index, mapInstance);\r\n }\r\n }, [mapInstance, props]);\r\n\r\n const safeMarkers = props.markers || [];\r\n\r\n return (\r\n /* ✅ استایل inline برای اطمینان از پر کردن فضای مودال والد */\r\n <div \r\n className={`map-layout-root ${isMobile ? 'is-mobile' : 'is-desktop'}`}\r\n style={{ position: 'absolute', inset: 0, height: '100%', width: '100%' }}\r\n >\r\n {!isMobile && (\r\n <aside className=\"sidebar\">\r\n <header className=\"sidebar-header\">لیست فروشگاه‌ها</header>\r\n <div className=\"sidebar-list\">\r\n {safeMarkers.map((store, idx) => (\r\n <StoreCard \r\n key={store.id} \r\n store={store} \r\n isSelected={props.selectedMarkerId === store.id}\r\n onSelect={() => handleStoreSelect(store, idx)}\r\n shopLogoUrl={store.logoUrl}\r\n />\r\n ))}\r\n </div>\r\n </aside>\r\n )}\r\n\r\n <main className=\"map-container\">\r\n <Map \r\n {...props} \r\n onMapLoad={(map) => {\r\n if (map) {\r\n setMapInstance(map);\r\n props.onMapLoad?.(map);\r\n }\r\n }} \r\n />\r\n \r\n {props.productName && (\r\n <div className=\"product-badge\">\r\n <div className=\"product-badge-left-side\">\r\n {props.productLogoUrl && (\r\n <img src={props.productLogoUrl} alt=\"product\" className=\"product-badge-logo\" />\r\n )}\r\n <span className=\"product-badge-fixed-label\">فروشگاه‌های حضوری</span>\r\n </div>\r\n <div className=\"product-badge-separator\"></div>\r\n <span className=\"product-badge-text\">{props.productName}</span>\r\n </div>\r\n )}\r\n\r\n {isMobile && (\r\n <div className=\"mobile-slider\" ref={sliderRef}>\r\n {safeMarkers.map((store, idx) => (\r\n <StoreCard \r\n key={store.id} \r\n store={store} \r\n isSelected={props.selectedMarkerId === store.id}\r\n onSelect={() => handleStoreSelect(store, idx)}\r\n shopLogoUrl={store.logoUrl}\r\n />\r\n ))}\r\n </div>\r\n )}\r\n </main>\r\n </div>\r\n );\r\n};","\"use client\";\r\n\r\nimport React, { useEffect, useRef, useState, useCallback } from 'react';\r\nimport type { MapboxMap, Store, MapProps } from '../types';\r\nimport { createCustomMarkerElement } from '../utils/createCustomMarkerElement';\r\nimport { createClusterElement } from '../utils/createClusterElement';\r\nimport Supercluster from 'supercluster';\r\n\r\n\r\nconst Map: React.FC<MapProps> = ({\r\n options,\r\n markers = [],\r\n markerLogoUrl = '',\r\n selectedMarkerId = null,\r\n onMarkerClick,\r\n onMapLoad,\r\n style = { width: '100%', height: '100%' },\r\n}) => {\r\n const mapContainerRef = useRef<HTMLDivElement>(null);\r\n const [mapInstance, setMapInstance] = useState<MapboxMap | null>(null);\r\n const [mapLib, setMapLib] = useState<any>(null);\r\n const markersRef = useRef<any[]>([]);\r\n const isRemoving = useRef(false); \r\n \r\n const clusterIndex = useRef(new Supercluster({\r\n radius: 60,\r\n maxZoom: 16\r\n }));\r\n\r\n \r\n useEffect(() => {\r\n let mounted = true;\r\n import('@neshan-maps-platform/mapbox-gl').then((mod) => {\r\n if (mounted) setMapLib(mod.default || mod);\r\n });\r\n return () => { mounted = false; };\r\n }, []);\r\n\r\n \r\n useEffect(() => {\r\n if (!mapLib || !mapContainerRef.current || mapInstance) return;\r\n\r\n const map = new mapLib.Map({\r\n container: mapContainerRef.current,\r\n ...options,\r\n });\r\n\r\n map.on('load', () => {\r\n if (!isRemoving.current) {\r\n setMapInstance(map);\r\n onMapLoad?.(map);\r\n }\r\n });\r\n\r\n return () => {\r\n if (map) {\r\n isRemoving.current = true;\r\n \r\n setTimeout(() => {\r\n try {\r\n map.remove();\r\n } catch (e) {\r\n console.warn(\"Mapbox remove error ignored:\", e);\r\n }\r\n }, 0);\r\n }\r\n };\r\n \r\n }, [mapLib]); \r\n\r\n \r\n useEffect(() => {\r\n if (!mapInstance || !selectedMarkerId || isRemoving.current) return;\r\n const selectedStore = markers.find(m => m.id === selectedMarkerId);\r\n if (selectedStore) {\r\n mapInstance.flyTo({\r\n center: [selectedStore.lng, selectedStore.lat],\r\n zoom: 16, \r\n essential: true,\r\n duration: 1200\r\n });\r\n }\r\n }, [selectedMarkerId, mapInstance, markers]);\r\n\r\n \r\n const updateMarkers = useCallback(() => {\r\n if (!mapInstance || !mapLib || isRemoving.current) return;\r\n\r\n try {\r\n const bounds = mapInstance.getBounds();\r\n const zoom = Math.floor(mapInstance.getZoom());\r\n\r\n const clusters = clusterIndex.current.getClusters(\r\n [bounds.getWest(), bounds.getSouth(), bounds.getEast(), bounds.getNorth()],\r\n zoom\r\n );\r\n\r\n markersRef.current.forEach(m => m.remove());\r\n markersRef.current = [];\r\n\r\n clusters.forEach(cluster => {\r\n const [lng, lat] = cluster.geometry.coordinates;\r\n const { cluster: isCluster, point_count: count } = cluster.properties;\r\n\r\n let el: HTMLElement;\r\n\r\n if (isCluster) {\r\n el = createClusterElement(count);\r\n el.onclick = (e) => {\r\n e.stopPropagation();\r\n const expansionZoom = Math.min(\r\n clusterIndex.current.getClusterExpansionZoom(cluster.id as number),\r\n 18\r\n );\r\n mapInstance.easeTo({ center: [lng, lat], zoom: expansionZoom });\r\n };\r\n } else {\r\n const storeData = cluster.properties as Store;\r\n el = createCustomMarkerElement({\r\n marker: storeData,\r\n isSelected: storeData.id === selectedMarkerId,\r\n logoSrc: markerLogoUrl,\r\n });\r\n el.onclick = () => onMarkerClick?.(storeData, 0, mapInstance);\r\n }\r\n\r\n const newMarker = new mapLib.Marker({ element: el, anchor: isCluster ? 'center' : 'bottom' })\r\n .setLngLat([lng, lat])\r\n .addTo(mapInstance);\r\n \r\n markersRef.current.push(newMarker);\r\n });\r\n } catch (err) {\r\n console.error(\"Update markers failed:\", err);\r\n }\r\n }, [mapInstance, mapLib, selectedMarkerId, markerLogoUrl, onMarkerClick]);\r\n\r\n \r\n useEffect(() => {\r\n if (isRemoving.current) return;\r\n const points: any = markers.map(s => ({\r\n type: 'Feature',\r\n properties: { ...s },\r\n geometry: { type: 'Point', coordinates: [s.lng, s.lat] }\r\n }));\r\n clusterIndex.current.load(points);\r\n updateMarkers();\r\n }, [markers, updateMarkers]);\r\n\r\n \r\n useEffect(() => {\r\n if (!mapInstance || isRemoving.current) return;\r\n mapInstance.on('moveend', updateMarkers);\r\n return () => { mapInstance.off('moveend', updateMarkers); };\r\n }, [mapInstance, updateMarkers]);\r\n\r\n return (\r\n <div style={{ position: 'relative', overflow: 'hidden', ...style }}>\r\n <div \r\n ref={mapContainerRef} \r\n style={{ width: '100%', height: '100%', backfaceVisibility: 'hidden' }} \r\n />\r\n </div>\r\n );\r\n};\r\n\r\nexport default Map;","import type { Store } from '../types';\r\n\r\ninterface CreateMarkerOptions {\r\n marker: Store;\r\n isSelected: boolean;\r\n logoSrc?: string;\r\n}\r\n\r\nexport function createCustomMarkerElement({\r\n marker,\r\n isSelected,\r\n logoSrc = '',\r\n}: CreateMarkerOptions): HTMLElement {\r\n const container = document.createElement('div');\r\n container.className = `neshan-marker-container ${isSelected ? 'neshan-marker-selected' : ''}`;\r\n\r\n const body = document.createElement('div');\r\n body.className = 'neshan-marker-body';\r\n\r\n const iconBox = document.createElement('div');\r\n Object.assign(iconBox.style, {\r\n width: '40px',\r\n height: '40px',\r\n borderRadius: '50%',\r\n backgroundColor: 'white',\r\n border: isSelected ? '3px solid #2563eb' : '',\r\n boxShadow: '0 4px 8px rgba(0,0,0,0.2)',\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n overflow: 'hidden'\r\n });\r\n\r\n if (logoSrc) {\r\n const img = document.createElement('img');\r\n img.src = logoSrc;\r\n img.style.width = '100%';\r\n img.style.height = '100%';\r\n img.style.objectFit = 'cover';\r\n iconBox.appendChild(img);\r\n } else {\r\n iconBox.style.backgroundColor = isSelected ? '#2563eb' : '#ef4444';\r\n }\r\n\r\n body.appendChild(iconBox);\r\n\r\n \r\n if (marker.name || marker.price) {\r\n const label = document.createElement('div');\r\n label.className = 'neshan-marker-label';\r\n \r\n const pricePart = marker.price ? ` (${marker.price} ت)` : '';\r\n label.textContent = `${marker.name}${pricePart}`;\r\n \r\n body.appendChild(label);\r\n }\r\n\r\n container.appendChild(body);\r\n return container;\r\n}","export function createClusterElement(count: number): HTMLElement {\r\n const container = document.createElement('div');\r\n container.className = 'neshan-cluster-marker';\r\n \r\n const countSpan = document.createElement('span');\r\n countSpan.className = 'neshan-cluster-count';\r\n countSpan.innerText = count.toString();\r\n \r\n container.appendChild(countSpan);\r\n return container;\r\n}","import React from 'react';\r\nimport { Store } from '../types';\r\n\r\ninterface StoreCardProps {\r\n store: Store;\r\n isSelected: boolean;\r\n onSelect: () => void;\r\n shopLogoUrl?: string; // لوگوی کوچک کنار توضیحات\r\n}\r\n\r\nexport const StoreCard: React.FC<StoreCardProps> = ({ store, isSelected, onSelect, shopLogoUrl }) => {\r\n const googleMapUrl = `https://www.google.com/maps/dir/?api=1&destination=${store.lat},${store.lng}`;\r\n\r\n const handleActionClick = (e: React.MouseEvent) => {\r\n e.stopPropagation();\r\n };\r\n\r\n return (\r\n <div \r\n className={`store-card ${isSelected ? 'active' : ''}`}\r\n onClick={onSelect}\r\n data-id={store.id}\r\n >\r\n {/* ردیف اول: نام، شهر و گزارش */}\r\n <div className=\"store-card-header\">\r\n <div className=\"store-info-main\">\r\n <h4 className=\"store-name\">{store.name}</h4>\r\n <div className=\"store-location-tag\">\r\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\"><path d=\"M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z\"></path><circle cx=\"12\" cy=\"10\" r=\"3\"></circle></svg>\r\n <span className=\"store-city\">{store.city}</span>\r\n </div>\r\n </div>\r\n <button className=\"report-btn\" onClick={handleActionClick}>\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#6b7280\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\"><path d=\"M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z\"></path><line x1=\"4\" y1=\"22\" x2=\"4\" y2=\"15\"></line></svg>\r\n <span>گزارش</span>\r\n </button>\r\n </div>\r\n\r\n {/* بخش عملکرد (اختیاری - اگر در دیتا بود) */}\r\n {store.performance && (\r\n <div className=\"store-performance\">\r\n عملکرد <span className=\"perf-value\">{store.performance}</span>\r\n </div>\r\n )}\r\n\r\n {/* بخش توضیحات همراه با لوگو */}\r\n <div className=\"store-desc-section\">\r\n {shopLogoUrl && (\r\n <img src={shopLogoUrl} alt=\"shop logo\" className=\"store-mini-logo\" />\r\n )}\r\n <p className=\"store-desc\">{store.desc}</p>\r\n </div>\r\n\r\n {/* ردیف آخر: قیمت و اطلاعات تماس */}\r\n <div className=\"store-card-footer\">\r\n <div className=\"price-container\">\r\n <span className=\"price-value\">{store.price}</span>\r\n <span className=\"price-unit\">تومان</span>\r\n </div>\r\n <div className=\"contact-trigger\">\r\n <span>اطلاعات تماس</span>\r\n <svg className={`arrow-icon ${isSelected ? 'open' : ''}`} width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\"><polyline points=\"18 15 12 9 6 15\"></polyline></svg>\r\n </div>\r\n </div>\r\n\r\n {/* بخش دکمه‌های عملیاتی (عمودی) */}\r\n <div className=\"store-details-wrapper\">\r\n <div className=\"store-details-content\">\r\n <div className=\"action-buttons vertical\">\r\n {store.phone && (\r\n <a href={`tel:${store.phone}`} className=\"btn btn-call full-width\" onClick={handleActionClick}>\r\n <div className=\"btn-content\">\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\"><path d=\"M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z\"></path></svg>\r\n <span>تماس: {store.phone}</span>\r\n </div>\r\n </a>\r\n )}\r\n {store.whatsapp && (\r\n <a href={`https://wa.me/${store.whatsapp}`} target=\"_blank\" rel=\"noreferrer\" className=\"btn btn-wa full-width\" onClick={handleActionClick}>\r\n <div className=\"btn-content\">\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\"><path d=\"M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z\"></path></svg>\r\n <span>ارسال پیام در واتساپ</span>\r\n </div>\r\n </a>\r\n )}\r\n <a href={googleMapUrl} target=\"_blank\" rel=\"noreferrer\" className=\"btn btn-route full-width\" onClick={handleActionClick}>\r\n <div className=\"btn-content\">\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\"><polygon points=\"3 11 22 2 13 21 11 13 3 11\"></polygon></svg>\r\n <span>مسیریابی روی نقشه</span>\r\n </div>\r\n </a>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n};"],"mappings":"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","names":["useState","useEffect","useRef","useCallback","jsx","jsx","jsxs","useState","useRef","useEffect","useCallback"]}
package/dist/styles.css CHANGED
@@ -1,6 +1,6 @@
1
1
  @import "@neshan-maps-platform/mapbox-gl/dist/NeshanMapboxGl.css";
2
2
 
3
- /* ۱. جلوگیری از بیرون‌زدگی المان‌ها */
3
+ /* جلوگیری از بیرون‌زدگی و نشت اسکرول */
4
4
  .map-layout-root,
5
5
  .map-layout-root *,
6
6
  .map-layout-root *::before,
@@ -11,23 +11,27 @@
11
11
  .map-layout-root {
12
12
  display: flex;
13
13
  width: 100%;
14
- height: 100%;
14
+ height: 100%; /* این ۱۰۰٪ باید از والد (محیط تست) گرفته شود */
15
+ min-height: 100%;
15
16
  direction: rtl;
16
17
  font-family: inherit;
17
- overflow: hidden;
18
+ overflow: hidden; /* جلوگیری از اسکرول خوردن کل کانتینر نقشه */
19
+ position: absolute; /* برای اطمینان از پر کردن فضای والد */
20
+ top: 0;
21
+ left: 0;
18
22
  }
19
23
 
20
- /* سایدبار دسکتاپ - اصلاح شده برای اسکرول */
24
+ /* سایدبار دسکتاپ */
21
25
  .sidebar {
22
26
  width: 350px;
23
- height: 100%;
27
+ height: 100%;
24
28
  background: #ffffff;
25
29
  border-left: 1px solid #e5e7eb;
26
30
  display: flex;
27
31
  flex-direction: column;
28
32
  z-index: 1000;
29
33
  box-shadow: 2px 0 10px rgba(0, 0, 0, 0.05);
30
- min-height: 0; /* ✅ بسیار حیاتی برای فعال شدن اسکرول در فلکس‌باکس */
34
+ position: relative;
31
35
  }
32
36
 
33
37
  .sidebar-header {
@@ -36,13 +40,16 @@
36
40
  font-size: 18px;
37
41
  color: #111827;
38
42
  border-bottom: 1px solid #f3f4f6;
39
- flex-shrink: 0; /* جلوگیری از فشرده شدن هدر */
43
+ flex-shrink: 0;
40
44
  }
41
45
 
42
46
  .sidebar-list {
43
47
  flex: 1;
44
- overflow-y: auto; /* فعال کردن اسکرول عمودی */
45
- min-height: 0; /* ✅ بسیار حیاتی: اجازه می‌دهد لیست کوچک‌تر از محتوا شود تا اسکرول بخورد */
48
+ overflow-y: auto;
49
+ min-height: 0;
50
+ /* ✅ جلوگیری از اسکرول شدن صفحه پشتی وقتی به انتهای لیست می‌رسید */
51
+ overscroll-behavior: contain;
52
+ -webkit-overflow-scrolling: touch;
46
53
  }
47
54
 
48
55
  /* کارت فروشگاه */
@@ -53,8 +60,6 @@
53
60
  transition: background 0.2s ease;
54
61
  background: #ffffff;
55
62
  width: 100%;
56
- display: flex;
57
- flex-direction: column;
58
63
  }
59
64
 
60
65
  .store-card.active {
@@ -137,7 +142,6 @@
137
142
  align-items: center;
138
143
  border-top: 1px dashed #e5e7eb;
139
144
  padding-top: 12px;
140
- width: 100%;
141
145
  }
142
146
 
143
147
  .price-container {
@@ -172,10 +176,9 @@
172
176
  transform: rotate(180deg);
173
177
  }
174
178
 
175
- .arrow-icon.open {
176
- transform: rotate(0deg);
177
- }
179
+ .arrow-icon.open { transform: rotate(0deg); }
178
180
 
181
+ /* دکمه‌های عمودی */
179
182
  .action-buttons.vertical {
180
183
  display: flex;
181
184
  flex-direction: column;
@@ -206,7 +209,13 @@
206
209
  .btn-wa { background: #10b981; color: white; }
207
210
  .btn-route { background: #7c3aed; color: white; }
208
211
 
209
- .map-container { flex: 1; position: relative; background: #f3f4f6; }
212
+ /* بخش نقشه */
213
+ .map-container {
214
+ flex: 1;
215
+ position: relative;
216
+ background: #f3f4f6;
217
+ height: 100%;
218
+ }
210
219
  .store-details-wrapper {
211
220
  display: grid;
212
221
  grid-template-rows: 0fr;
@@ -217,6 +226,7 @@
217
226
  .active .store-details-wrapper { grid-template-rows: 1fr; }
218
227
  .store-details-content { min-height: 0; width: 100%; }
219
228
 
229
+ /* ✅ استایل مارکر: سفید، بردر نارنجی، متن مشکی */
220
230
  .neshan-marker-container { pointer-events: auto; }
221
231
  .neshan-marker-body {
222
232
  display: flex;
@@ -225,21 +235,21 @@
225
235
  transform-origin: bottom center;
226
236
  }
227
237
 
228
- /* ✅ استایل مارکر: پس‌زمینه سفید، بردر نارنجی، متن مشکی */
229
238
  .neshan-marker-label {
230
239
  background: #ffffff;
231
240
  color: #111827 !important;
232
241
  font-size: 11px;
233
242
  font-weight: 800;
234
- padding: 4px 10px;
243
+ padding: 4px 12px;
235
244
  border-radius: 20px;
236
245
  margin-top: 6px;
237
246
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
238
- border: 2px solid #f97316;
247
+ border: 2px solid #f97316; /* نارنجی */
239
248
  white-space: nowrap;
240
249
  pointer-events: none;
241
250
  }
242
251
 
252
+ /* کلاستر */
243
253
  .neshan-cluster-marker {
244
254
  display: flex;
245
255
  align-items: center;
@@ -259,6 +269,7 @@
259
269
  font-size: 16px;
260
270
  }
261
271
 
272
+ /* انیمیشن انتخاب */
262
273
  .neshan-marker-selected .neshan-marker-body {
263
274
  animation: marker-bounce 0.8s infinite alternate cubic-bezier(0.45, 0.05, 0.55, 0.95);
264
275
  z-index: 100;
@@ -269,12 +280,14 @@
269
280
  to { transform: translateY(-12px) scale(1.15); }
270
281
  }
271
282
 
283
+ /* موبایل */
272
284
  .is-mobile .mobile-slider {
273
285
  position: absolute; bottom: 0; left: 0; right: 0;
274
286
  display: flex; gap: 12px; padding: 20px 20px 40px 20px;
275
287
  overflow-x: auto; scroll-snap-type: x mandatory;
276
288
  z-index: 20; align-items: flex-end;
277
289
  scrollbar-width: none;
290
+ overscroll-behavior: contain;
278
291
  }
279
292
  .is-mobile .mobile-slider::-webkit-scrollbar { display: none; }
280
293
  .is-mobile .store-card {
@@ -286,6 +299,7 @@
286
299
  border: none;
287
300
  }
288
301
 
302
+ /* باج محصول حرفه‌ای */
289
303
  .product-badge {
290
304
  position: absolute;
291
305
  bottom: 24px;
@@ -300,54 +314,15 @@
300
314
  gap: 16px;
301
315
  z-index: 40;
302
316
  border: 1px solid rgba(0, 0, 0, 0.05);
303
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
304
317
  }
305
318
 
306
- .product-badge-left-side {
307
- display: flex;
308
- flex-direction: column;
309
- align-items: center;
310
- gap: 4px;
311
- }
312
-
313
- .product-badge-logo {
314
- width: 65px;
315
- height: 50px;
316
- object-fit: contain;
317
- border-radius: 12px;
318
- padding: 1px;
319
- }
320
-
321
- .product-badge-fixed-label {
322
- font-size: 9px;
323
- font-weight: 800;
324
- color: #64748b;
325
- white-space: nowrap;
326
- }
327
-
328
- .product-badge-separator {
329
- width: 1px;
330
- height: 35px;
331
- background: #e2e8f0;
332
- }
333
-
334
- .product-badge-text {
335
- font-weight: 850;
336
- font-size: 12px;
337
- color: #0f172a;
338
- white-space: nowrap;
339
- }
340
-
341
- .is-mobile .product-badge {
342
- top: 16px;
343
- bottom: auto;
344
- right: 16px;
345
- padding: 10px 14px;
346
- border-radius: 16px;
347
- gap: 12px;
348
- max-width: 85vw;
349
- }
319
+ .product-badge-left-side { display: flex; flex-direction: column; align-items: center; gap: 4px; }
320
+ .product-badge-logo { width: 65px; height: 50px; object-fit: contain; border-radius: 12px; padding: 1px; }
321
+ .product-badge-fixed-label { font-size: 9px; font-weight: 800; color: #64748b; white-space: nowrap; }
322
+ .product-badge-separator { width: 1px; height: 35px; background: #e2e8f0; }
323
+ .product-badge-text { font-weight: 850; font-size: 12px; color: #0f172a; white-space: nowrap; }
350
324
 
325
+ .is-mobile .product-badge { top: 16px; bottom: auto; right: 16px; padding: 10px 14px; border-radius: 16px; gap: 12px; max-width: 85vw; }
351
326
  .is-mobile .product-badge-logo { width: 32px; height: 32px; }
352
327
  .is-mobile .product-badge-fixed-label { font-size: 8px; }
353
328
  .is-mobile .product-badge-separator { height: 25px; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mehdi-akbari-map",
3
- "version": "1.3.1",
3
+ "version": "1.3.2",
4
4
  "description": "A professional Map",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",