mehdi-akbari-map 1.3.2 → 1.3.3

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 // هماهنگی اسکرول\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":";;;AAEA,SAAgB,YAAAA,WAAU,aAAAC,YAAW,UAAAC,SAAQ,eAAAC,oBAAmB;;;ACAhE,SAAgB,WAAW,QAAQ,UAAU,mBAAmB;;;ACMzD,SAAS,0BAA0B;AAAA,EACxC;AAAA,EACA;AAAA,EACA,UAAU;AACZ,GAAqC;AACnC,QAAM,YAAY,SAAS,cAAc,KAAK;AAC9C,YAAU,YAAY,2BAA2B,aAAa,2BAA2B,EAAE;AAE3F,QAAM,OAAO,SAAS,cAAc,KAAK;AACzC,OAAK,YAAY;AAEjB,QAAM,UAAU,SAAS,cAAc,KAAK;AAC5C,SAAO,OAAO,QAAQ,OAAO;AAAA,IAC3B,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,cAAc;AAAA,IACd,iBAAiB;AAAA,IACjB,QAAQ,aAAa,sBAAsB;AAAA,IAC3C,WAAW;AAAA,IACX,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,UAAU;AAAA,EACZ,CAAC;AAED,MAAI,SAAS;AACX,UAAM,MAAM,SAAS,cAAc,KAAK;AACxC,QAAI,MAAM;AACV,QAAI,MAAM,QAAQ;AAClB,QAAI,MAAM,SAAS;AACnB,QAAI,MAAM,YAAY;AACtB,YAAQ,YAAY,GAAG;AAAA,EACzB,OAAO;AACL,YAAQ,MAAM,kBAAkB,aAAa,YAAY;AAAA,EAC3D;AAEA,OAAK,YAAY,OAAO;AAGxB,MAAI,OAAO,QAAQ,OAAO,OAAO;AAC/B,UAAM,QAAQ,SAAS,cAAc,KAAK;AAC1C,UAAM,YAAY;AAElB,UAAM,YAAY,OAAO,QAAQ,KAAK,OAAO,KAAK,aAAQ;AAC1D,UAAM,cAAc,GAAG,OAAO,IAAI,GAAG,SAAS;AAE9C,SAAK,YAAY,KAAK;AAAA,EACxB;AAEA,YAAU,YAAY,IAAI;AAC1B,SAAO;AACT;;;AC3DO,SAAS,qBAAqB,OAA4B;AAC/D,QAAM,YAAY,SAAS,cAAc,KAAK;AAC9C,YAAU,YAAY;AAEtB,QAAM,YAAY,SAAS,cAAc,MAAM;AAC/C,YAAU,YAAY;AACtB,YAAU,YAAY,MAAM,SAAS;AAErC,YAAU,YAAY,SAAS;AAC/B,SAAO;AACT;;;AFJA,OAAO,kBAAkB;AAwJnB;AArJN,IAAM,MAA0B,CAAC;AAAA,EAC/B;AAAA,EACA,UAAU,CAAC;AAAA,EACX,gBAAgB;AAAA,EAChB,mBAAmB;AAAA,EACnB;AAAA,EACA;AAAA,EACA,QAAQ,EAAE,OAAO,QAAQ,QAAQ,OAAO;AAC1C,MAAM;AACJ,QAAM,kBAAkB,OAAuB,IAAI;AACnD,QAAM,CAAC,aAAa,cAAc,IAAI,SAA2B,IAAI;AACrE,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAc,IAAI;AAC9C,QAAM,aAAa,OAAc,CAAC,CAAC;AACnC,QAAM,aAAa,OAAO,KAAK;AAE/B,QAAM,eAAe,OAAO,IAAI,aAAa;AAAA,IAC3C,QAAQ;AAAA,IACR,SAAS;AAAA,EACX,CAAC,CAAC;AAGF,YAAU,MAAM;AACd,QAAI,UAAU;AACd,WAAO,iCAAiC,EAAE,KAAK,CAAC,QAAQ;AACtD,UAAI,QAAS,WAAU,IAAI,WAAW,GAAG;AAAA,IAC3C,CAAC;AACD,WAAO,MAAM;AAAE,gBAAU;AAAA,IAAO;AAAA,EAClC,GAAG,CAAC,CAAC;AAGL,YAAU,MAAM;AACd,QAAI,CAAC,UAAU,CAAC,gBAAgB,WAAW,YAAa;AAExD,UAAM,MAAM,IAAI,OAAO,IAAI;AAAA,MACzB,WAAW,gBAAgB;AAAA,MAC3B,GAAG;AAAA,IACL,CAAC;AAED,QAAI,GAAG,QAAQ,MAAM;AACnB,UAAI,CAAC,WAAW,SAAS;AACvB,uBAAe,GAAG;AAClB,oBAAY,GAAG;AAAA,MACjB;AAAA,IACF,CAAC;AAED,WAAO,MAAM;AACX,UAAI,KAAK;AACP,mBAAW,UAAU;AAErB,mBAAW,MAAM;AACf,cAAI;AACF,gBAAI,OAAO;AAAA,UACb,SAAS,GAAG;AACV,oBAAQ,KAAK,gCAAgC,CAAC;AAAA,UAChD;AAAA,QACF,GAAG,CAAC;AAAA,MACN;AAAA,IACF;AAAA,EAEF,GAAG,CAAC,MAAM,CAAC;AAGX,YAAU,MAAM;AACd,QAAI,CAAC,eAAe,CAAC,oBAAoB,WAAW,QAAS;AAC7D,UAAM,gBAAgB,QAAQ,KAAK,OAAK,EAAE,OAAO,gBAAgB;AACjE,QAAI,eAAe;AACjB,kBAAY,MAAM;AAAA,QAChB,QAAQ,CAAC,cAAc,KAAK,cAAc,GAAG;AAAA,QAC7C,MAAM;AAAA,QACN,WAAW;AAAA,QACX,UAAU;AAAA,MACZ,CAAC;AAAA,IACH;AAAA,EACF,GAAG,CAAC,kBAAkB,aAAa,OAAO,CAAC;AAG3C,QAAM,gBAAgB,YAAY,MAAM;AACtC,QAAI,CAAC,eAAe,CAAC,UAAU,WAAW,QAAS;AAEnD,QAAI;AACF,YAAM,SAAS,YAAY,UAAU;AACrC,YAAM,OAAO,KAAK,MAAM,YAAY,QAAQ,CAAC;AAE7C,YAAM,WAAW,aAAa,QAAQ;AAAA,QACpC,CAAC,OAAO,QAAQ,GAAG,OAAO,SAAS,GAAG,OAAO,QAAQ,GAAG,OAAO,SAAS,CAAC;AAAA,QACzE;AAAA,MACF;AAEA,iBAAW,QAAQ,QAAQ,OAAK,EAAE,OAAO,CAAC;AAC1C,iBAAW,UAAU,CAAC;AAEtB,eAAS,QAAQ,aAAW;AAC1B,cAAM,CAAC,KAAK,GAAG,IAAI,QAAQ,SAAS;AACpC,cAAM,EAAE,SAAS,WAAW,aAAa,MAAM,IAAI,QAAQ;AAE3D,YAAI;AAEJ,YAAI,WAAW;AACb,eAAK,qBAAqB,KAAK;AAC/B,aAAG,UAAU,CAAC,MAAM;AAClB,cAAE,gBAAgB;AAClB,kBAAM,gBAAgB,KAAK;AAAA,cACzB,aAAa,QAAQ,wBAAwB,QAAQ,EAAY;AAAA,cACjE;AAAA,YACF;AACA,wBAAY,OAAO,EAAE,QAAQ,CAAC,KAAK,GAAG,GAAG,MAAM,cAAc,CAAC;AAAA,UAChE;AAAA,QACF,OAAO;AACL,gBAAM,YAAY,QAAQ;AAC1B,eAAK,0BAA0B;AAAA,YAC7B,QAAQ;AAAA,YACR,YAAY,UAAU,OAAO;AAAA,YAC7B,SAAS;AAAA,UACX,CAAC;AACD,aAAG,UAAU,MAAM,gBAAgB,WAAW,GAAG,WAAW;AAAA,QAC9D;AAEA,cAAM,YAAY,IAAI,OAAO,OAAO,EAAE,SAAS,IAAI,QAAQ,YAAY,WAAW,SAAS,CAAC,EACzF,UAAU,CAAC,KAAK,GAAG,CAAC,EACpB,MAAM,WAAW;AAEpB,mBAAW,QAAQ,KAAK,SAAS;AAAA,MACnC,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,cAAQ,MAAM,0BAA0B,GAAG;AAAA,IAC7C;AAAA,EACF,GAAG,CAAC,aAAa,QAAQ,kBAAkB,eAAe,aAAa,CAAC;AAGxE,YAAU,MAAM;AACd,QAAI,WAAW,QAAS;AACxB,UAAM,SAAc,QAAQ,IAAI,QAAM;AAAA,MACpC,MAAM;AAAA,MACN,YAAY,EAAE,GAAG,EAAE;AAAA,MACnB,UAAU,EAAE,MAAM,SAAS,aAAa,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE;AAAA,IACzD,EAAE;AACF,iBAAa,QAAQ,KAAK,MAAM;AAChC,kBAAc;AAAA,EAChB,GAAG,CAAC,SAAS,aAAa,CAAC;AAG3B,YAAU,MAAM;AACd,QAAI,CAAC,eAAe,WAAW,QAAS;AACxC,gBAAY,GAAG,WAAW,aAAa;AACvC,WAAO,MAAM;AAAE,kBAAY,IAAI,WAAW,aAAa;AAAA,IAAG;AAAA,EAC5D,GAAG,CAAC,aAAa,aAAa,CAAC;AAE/B,SACE,oBAAC,SAAI,OAAO,EAAE,UAAU,YAAY,UAAU,UAAU,GAAG,MAAM,GAC/D;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL,OAAO,EAAE,OAAO,QAAQ,QAAQ,QAAQ,oBAAoB,SAAS;AAAA;AAAA,EACvE,GACF;AAEJ;AAEA,IAAO,cAAQ;;;AG5IL,gBAAAC,MAEE,YAFF;AAhBH,IAAM,YAAsC,CAAC,EAAE,OAAO,YAAY,UAAU,YAAY,MAAM;AACnG,QAAM,eAAe,sDAAsD,MAAM,GAAG,IAAI,MAAM,GAAG;AAEjG,QAAM,oBAAoB,CAAC,MAAwB;AACjD,MAAE,gBAAgB;AAAA,EACpB;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,cAAc,aAAa,WAAW,EAAE;AAAA,MACnD,SAAS;AAAA,MACT,WAAS,MAAM;AAAA,MAGf;AAAA,6BAAC,SAAI,WAAU,qBACb;AAAA,+BAAC,SAAI,WAAU,mBACb;AAAA,4BAAAA,KAAC,QAAG,WAAU,cAAc,gBAAM,MAAK;AAAA,YACvC,qBAAC,SAAI,WAAU,sBACb;AAAA,mCAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ;AAAA,gCAAAA,KAAC,UAAK,GAAE,kDAAiD;AAAA,gBAAO,gBAAAA,KAAC,YAAO,IAAG,MAAK,IAAG,MAAK,GAAE,KAAI;AAAA,iBAAS;AAAA,cACtP,gBAAAA,KAAC,UAAK,WAAU,cAAc,gBAAM,MAAK;AAAA,eAC3C;AAAA,aACF;AAAA,UACA,qBAAC,YAAO,WAAU,cAAa,SAAS,mBACtC;AAAA,iCAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,QAAO,WAAU,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ;AAAA,8BAAAA,KAAC,UAAK,GAAE,6DAA4D;AAAA,cAAO,gBAAAA,KAAC,UAAK,IAAG,KAAI,IAAG,MAAK,IAAG,KAAI,IAAG,MAAK;AAAA,eAAO;AAAA,YAChQ,gBAAAA,KAAC,UAAK,4CAAK;AAAA,aACb;AAAA,WACF;AAAA,QAGC,MAAM,eACL,qBAAC,SAAI,WAAU,qBAAoB;AAAA;AAAA,UAC1B,gBAAAA,KAAC,UAAK,WAAU,cAAc,gBAAM,aAAY;AAAA,WACzD;AAAA,QAIF,qBAAC,SAAI,WAAU,sBACZ;AAAA,yBACC,gBAAAA,KAAC,SAAI,KAAK,aAAa,KAAI,aAAY,WAAU,mBAAkB;AAAA,UAErE,gBAAAA,KAAC,OAAE,WAAU,cAAc,gBAAM,MAAK;AAAA,WACxC;AAAA,QAGA,qBAAC,SAAI,WAAU,qBACb;AAAA,+BAAC,SAAI,WAAU,mBACb;AAAA,4BAAAA,KAAC,UAAK,WAAU,eAAe,gBAAM,OAAM;AAAA,YAC3C,gBAAAA,KAAC,UAAK,WAAU,cAAa,4CAAK;AAAA,aACpC;AAAA,UACA,qBAAC,SAAI,WAAU,mBACb;AAAA,4BAAAA,KAAC,UAAK,iFAAY;AAAA,YAClB,gBAAAA,KAAC,SAAI,WAAW,cAAc,aAAa,SAAS,EAAE,IAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,OAAM,eAAc,SAAQ,gBAAe,SAAQ,0BAAAA,KAAC,cAAS,QAAO,mBAAkB,GAAW;AAAA,aACtP;AAAA,WACF;AAAA,QAGA,gBAAAA,KAAC,SAAI,WAAU,yBACb,0BAAAA,KAAC,SAAI,WAAU,yBACb,+BAAC,SAAI,WAAU,2BACZ;AAAA,gBAAM,SACL,gBAAAA,KAAC,OAAE,MAAM,OAAO,MAAM,KAAK,IAAI,WAAU,2BAA0B,SAAS,mBAC1E,+BAAC,SAAI,WAAU,eACb;AAAA,4BAAAA,KAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ,0BAAAA,KAAC,UAAK,GAAE,iSAAgS,GAAO;AAAA,YAC9b,qBAAC,UAAK;AAAA;AAAA,cAAO,MAAM;AAAA,eAAM;AAAA,aAC3B,GACF;AAAA,UAED,MAAM,YACL,gBAAAA,KAAC,OAAE,MAAM,iBAAiB,MAAM,QAAQ,IAAI,QAAO,UAAS,KAAI,cAAa,WAAU,yBAAwB,SAAS,mBACtH,+BAAC,SAAI,WAAU,eACb;AAAA,4BAAAA,KAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ,0BAAAA,KAAC,UAAK,GAAE,4LAA2L,GAAO;AAAA,YACzV,gBAAAA,KAAC,UAAK,uHAAoB;AAAA,aAC5B,GACF;AAAA,UAEF,gBAAAA,KAAC,OAAE,MAAM,cAAc,QAAO,UAAS,KAAI,cAAa,WAAU,4BAA2B,SAAS,mBACpG,+BAAC,SAAI,WAAU,eACb;AAAA,4BAAAA,KAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ,0BAAAA,KAAC,aAAQ,QAAO,8BAA6B,GAAU;AAAA,YACtM,gBAAAA,KAAC,UAAK,0GAAiB;AAAA,aACzB,GACF;AAAA,WACF,GACF,GACF;AAAA;AAAA;AAAA,EACF;AAEJ;;;AJjDQ,SACE,OAAAC,MADF,QAAAC,aAAA;AAxCD,IAAM,iBAAqC,CAAC,UAAU;AAC3D,QAAM,CAAC,UAAU,WAAW,IAAIC,UAAS,KAAK;AAC9C,QAAM,CAAC,aAAa,cAAc,IAAIA,UAAc,IAAI;AACxD,QAAM,YAAYC,QAAuB,IAAI;AAE7C,EAAAC,WAAU,MAAM;AACd,UAAM,cAAc,MAAM,YAAY,OAAO,aAAa,IAAI;AAC9D,gBAAY;AACZ,WAAO,iBAAiB,UAAU,WAAW;AAC7C,WAAO,MAAM,OAAO,oBAAoB,UAAU,WAAW;AAAA,EAC/D,GAAG,CAAC,CAAC;AAGL,EAAAA,WAAU,MAAM;AACd,QAAI,YAAY,MAAM,oBAAoB,UAAU,SAAS;AAC3D,YAAM,QAAQ,WAAW,MAAM;AAC7B,cAAM,eAAe,UAAU,SAAS,cAAc,aAAa,MAAM,gBAAgB,IAAI;AAC7F,YAAI,cAAc;AAChB,uBAAa,eAAe,EAAE,UAAU,UAAU,QAAQ,UAAU,OAAO,UAAU,CAAC;AAAA,QACxF;AAAA,MACF,GAAG,GAAG;AACN,aAAO,MAAM,aAAa,KAAK;AAAA,IACjC;AAAA,EACF,GAAG,CAAC,MAAM,kBAAkB,QAAQ,CAAC;AAErC,QAAM,oBAAoBC,aAAY,CAAC,OAAc,UAAkB;AACrE,QAAI,aAAa;AACf,YAAM,gBAAgB,OAAO,OAAO,WAAW;AAAA,IACjD;AAAA,EACF,GAAG,CAAC,aAAa,KAAK,CAAC;AAEvB,QAAM,cAAc,MAAM,WAAW,CAAC;AAEtC;AAAA;AAAA,IAEE,gBAAAJ;AAAA,MAAC;AAAA;AAAA,QACC,WAAW,mBAAmB,WAAW,cAAc,YAAY;AAAA,QACnE,OAAO,EAAE,UAAU,YAAY,OAAO,GAAG,QAAQ,QAAQ,OAAO,OAAO;AAAA,QAEtE;AAAA,WAAC,YACA,gBAAAA,MAAC,WAAM,WAAU,WACf;AAAA,4BAAAD,KAAC,YAAO,WAAU,kBAAiB,mGAAe;AAAA,YAClD,gBAAAA,KAAC,SAAI,WAAU,gBACZ,sBAAY,IAAI,CAAC,OAAO,QACvB,gBAAAA;AAAA,cAAC;AAAA;AAAA,gBAEC;AAAA,gBACA,YAAY,MAAM,qBAAqB,MAAM;AAAA,gBAC7C,UAAU,MAAM,kBAAkB,OAAO,GAAG;AAAA,gBAC5C,aAAa,MAAM;AAAA;AAAA,cAJd,MAAM;AAAA,YAKb,CACD,GACH;AAAA,aACF;AAAA,UAGF,gBAAAC,MAAC,UAAK,WAAU,iBACd;AAAA,4BAAAD;AAAA,cAAC;AAAA;AAAA,gBACE,GAAG;AAAA,gBACJ,WAAW,CAAC,QAAQ;AAClB,sBAAI,KAAK;AACP,mCAAe,GAAG;AAClB,0BAAM,YAAY,GAAG;AAAA,kBACvB;AAAA,gBACF;AAAA;AAAA,YACF;AAAA,YAEC,MAAM,eACL,gBAAAC,MAAC,SAAI,WAAU,iBACZ;AAAA,8BAAAA,MAAC,SAAI,WAAU,2BACX;AAAA,sBAAM,kBACL,gBAAAD,KAAC,SAAI,KAAK,MAAM,gBAAgB,KAAI,WAAU,WAAU,sBAAqB;AAAA,gBAE/E,gBAAAA,KAAC,UAAK,WAAU,6BAA4B,+GAAiB;AAAA,iBAChE;AAAA,cACA,gBAAAA,KAAC,SAAI,WAAU,2BAA0B;AAAA,cACzC,gBAAAA,KAAC,UAAK,WAAU,sBAAsB,gBAAM,aAAY;AAAA,eAC3D;AAAA,YAGD,YACC,gBAAAA,KAAC,SAAI,WAAU,iBAAgB,KAAK,WACjC,sBAAY,IAAI,CAAC,OAAO,QACvB,gBAAAA;AAAA,cAAC;AAAA;AAAA,gBAEC;AAAA,gBACA,YAAY,MAAM,qBAAqB,MAAM;AAAA,gBAC7C,UAAU,MAAM,kBAAkB,OAAO,GAAG;AAAA,gBAC5C,aAAa,MAAM;AAAA;AAAA,cAJd,MAAM;AAAA,YAKb,CACD,GACH;AAAA,aAEJ;AAAA;AAAA;AAAA,IACF;AAAA;AAEJ;","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 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 /* ✅ استفاده از استایل ساده برای پر کردن فضای والد بدون شکستن لایه‌بندی */\r\n <div className={`map-layout-root ${isMobile ? 'is-mobile' : 'is-desktop'}`} style={{ width: '100%', height: '100%' }}>\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,27 +11,25 @@
11
11
  .map-layout-root {
12
12
  display: flex;
13
13
  width: 100%;
14
- height: 100%; /* این ۱۰۰٪ باید از والد (محیط تست) گرفته شود */
15
- min-height: 100%;
14
+ height: 100%; /* فیت شدن با ارتفاع والد */
16
15
  direction: rtl;
17
16
  font-family: inherit;
18
- overflow: hidden; /* جلوگیری از اسکرول خوردن کل کانتینر نقشه */
19
- position: absolute; /* برای اطمینان از پر کردن فضای والد */
20
- top: 0;
21
- left: 0;
17
+ overflow: hidden; /* بسیار مهم: جلوگیری از اسکرول خوردن کل کانتینر پکیج */
18
+ position: relative;
22
19
  }
23
20
 
24
- /* سایدبار دسکتاپ */
21
+ /* سایدبار دسکتاپ - اصلاح شده برای مهار اسکرول */
25
22
  .sidebar {
26
23
  width: 350px;
27
- height: 100%;
24
+ height: 100%; /* برابر با ارتفاع والد */
28
25
  background: #ffffff;
29
26
  border-left: 1px solid #e5e7eb;
30
27
  display: flex;
31
28
  flex-direction: column;
32
29
  z-index: 1000;
33
30
  box-shadow: 2px 0 10px rgba(0, 0, 0, 0.05);
34
- position: relative;
31
+ flex-shrink: 0; /* جلوگیری از فشرده شدن سایدبار توسط نقشه */
32
+ min-height: 0; /* حیاتی برای محاسبات فلکس‌باکس */
35
33
  }
36
34
 
37
35
  .sidebar-header {
@@ -40,15 +38,16 @@
40
38
  font-size: 18px;
41
39
  color: #111827;
42
40
  border-bottom: 1px solid #f3f4f6;
43
- flex-shrink: 0;
41
+ flex-shrink: 0; /* هدر نباید اسکرول شود یا تغییر سایز دهد */
44
42
  }
45
43
 
46
44
  .sidebar-list {
47
- flex: 1;
48
- overflow-y: auto;
49
- min-height: 0;
50
- /* ✅ جلوگیری از اسکرول شدن صفحه پشتی وقتی به انتهای لیست می‌رسید */
45
+ flex: 1; /* اشغال تمام فضای باقی‌مانده سایدبار */
46
+ overflow-y: auto !important; /* فعال کردن اسکرول عمودی */
47
+ min-height: 0;
48
+ /* ✅ جلوگیری از انتقال اسکرول به صفحه اصلی سایت */
51
49
  overscroll-behavior: contain;
50
+ /* بهبود حرکت اسکرول در مک و موبایل */
52
51
  -webkit-overflow-scrolling: touch;
53
52
  }
54
53
 
@@ -60,6 +59,8 @@
60
59
  transition: background 0.2s ease;
61
60
  background: #ffffff;
62
61
  width: 100%;
62
+ display: flex;
63
+ flex-direction: column;
63
64
  }
64
65
 
65
66
  .store-card.active {
@@ -142,6 +143,7 @@
142
143
  align-items: center;
143
144
  border-top: 1px dashed #e5e7eb;
144
145
  padding-top: 12px;
146
+ width: 100%;
145
147
  }
146
148
 
147
149
  .price-container {
@@ -176,9 +178,10 @@
176
178
  transform: rotate(180deg);
177
179
  }
178
180
 
179
- .arrow-icon.open { transform: rotate(0deg); }
181
+ .arrow-icon.open {
182
+ transform: rotate(0deg);
183
+ }
180
184
 
181
- /* دکمه‌های عمودی */
182
185
  .action-buttons.vertical {
183
186
  display: flex;
184
187
  flex-direction: column;
@@ -209,13 +212,13 @@
209
212
  .btn-wa { background: #10b981; color: white; }
210
213
  .btn-route { background: #7c3aed; color: white; }
211
214
 
212
- /* بخش نقشه */
213
215
  .map-container {
214
216
  flex: 1;
215
217
  position: relative;
216
218
  background: #f3f4f6;
217
- height: 100%;
219
+ height: 100%; /* پر کردن کل فضای والد */
218
220
  }
221
+
219
222
  .store-details-wrapper {
220
223
  display: grid;
221
224
  grid-template-rows: 0fr;
@@ -226,7 +229,6 @@
226
229
  .active .store-details-wrapper { grid-template-rows: 1fr; }
227
230
  .store-details-content { min-height: 0; width: 100%; }
228
231
 
229
- /* ✅ استایل مارکر: سفید، بردر نارنجی، متن مشکی */
230
232
  .neshan-marker-container { pointer-events: auto; }
231
233
  .neshan-marker-body {
232
234
  display: flex;
@@ -235,21 +237,21 @@
235
237
  transform-origin: bottom center;
236
238
  }
237
239
 
240
+ /* ✅ استایل مارکر: پس‌زمینه سفید، بردر نارنجی، متن مشکی */
238
241
  .neshan-marker-label {
239
242
  background: #ffffff;
240
243
  color: #111827 !important;
241
244
  font-size: 11px;
242
245
  font-weight: 800;
243
- padding: 4px 12px;
246
+ padding: 4px 10px;
244
247
  border-radius: 20px;
245
248
  margin-top: 6px;
246
249
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
247
- border: 2px solid #f97316; /* نارنجی */
250
+ border: 2px solid #f97316; /* بردر نارنجی */
248
251
  white-space: nowrap;
249
252
  pointer-events: none;
250
253
  }
251
254
 
252
- /* کلاستر */
253
255
  .neshan-cluster-marker {
254
256
  display: flex;
255
257
  align-items: center;
@@ -269,7 +271,6 @@
269
271
  font-size: 16px;
270
272
  }
271
273
 
272
- /* انیمیشن انتخاب */
273
274
  .neshan-marker-selected .neshan-marker-body {
274
275
  animation: marker-bounce 0.8s infinite alternate cubic-bezier(0.45, 0.05, 0.55, 0.95);
275
276
  z-index: 100;
@@ -280,7 +281,6 @@
280
281
  to { transform: translateY(-12px) scale(1.15); }
281
282
  }
282
283
 
283
- /* موبایل */
284
284
  .is-mobile .mobile-slider {
285
285
  position: absolute; bottom: 0; left: 0; right: 0;
286
286
  display: flex; gap: 12px; padding: 20px 20px 40px 20px;
@@ -314,15 +314,54 @@
314
314
  gap: 16px;
315
315
  z-index: 40;
316
316
  border: 1px solid rgba(0, 0, 0, 0.05);
317
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
318
+ }
319
+
320
+ .product-badge-left-side {
321
+ display: flex;
322
+ flex-direction: column;
323
+ align-items: center;
324
+ gap: 4px;
325
+ }
326
+
327
+ .product-badge-logo {
328
+ width: 65px;
329
+ height: 50px;
330
+ object-fit: contain;
331
+ border-radius: 12px;
332
+ padding: 1px;
333
+ }
334
+
335
+ .product-badge-fixed-label {
336
+ font-size: 9px;
337
+ font-weight: 800;
338
+ color: #64748b;
339
+ white-space: nowrap;
317
340
  }
318
341
 
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; }
342
+ .product-badge-separator {
343
+ width: 1px;
344
+ height: 35px;
345
+ background: #e2e8f0;
346
+ }
347
+
348
+ .product-badge-text {
349
+ font-weight: 850;
350
+ font-size: 12px;
351
+ color: #0f172a;
352
+ white-space: nowrap;
353
+ }
354
+
355
+ .is-mobile .product-badge {
356
+ top: 16px;
357
+ bottom: auto;
358
+ right: 16px;
359
+ padding: 10px 14px;
360
+ border-radius: 16px;
361
+ gap: 12px;
362
+ max-width: 85vw;
363
+ }
324
364
 
325
- .is-mobile .product-badge { top: 16px; bottom: auto; right: 16px; padding: 10px 14px; border-radius: 16px; gap: 12px; max-width: 85vw; }
326
365
  .is-mobile .product-badge-logo { width: 32px; height: 32px; }
327
366
  .is-mobile .product-badge-fixed-label { font-size: 8px; }
328
367
  .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.2",
3
+ "version": "1.3.3",
4
4
  "description": "A professional Map",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",