mehdi-akbari-map 1.2.9 → 1.3.0
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/index.cjs +4 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +11 -7
- package/dist/index.css.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +4 -2
- package/dist/react.cjs.map +1 -1
- package/dist/react.js +4 -2
- package/dist/react.js.map +1 -1
- package/dist/styles.css +15 -22
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -56,6 +56,7 @@ function createCustomMarkerElement({
|
|
|
56
56
|
width: "40px",
|
|
57
57
|
height: "40px",
|
|
58
58
|
borderRadius: "50%",
|
|
59
|
+
backgroundColor: "white",
|
|
59
60
|
border: isSelected ? "3px solid #2563eb" : "",
|
|
60
61
|
boxShadow: "0 4px 8px rgba(0,0,0,0.2)",
|
|
61
62
|
display: "flex",
|
|
@@ -74,10 +75,11 @@ function createCustomMarkerElement({
|
|
|
74
75
|
iconBox.style.backgroundColor = isSelected ? "#2563eb" : "#ef4444";
|
|
75
76
|
}
|
|
76
77
|
body.appendChild(iconBox);
|
|
77
|
-
if (marker.
|
|
78
|
+
if (marker.name || marker.price) {
|
|
78
79
|
const label = document.createElement("div");
|
|
79
80
|
label.className = "neshan-marker-label";
|
|
80
|
-
|
|
81
|
+
const priceText = marker.price ? ` (${marker.price} \u062A)` : "";
|
|
82
|
+
label.textContent = `${marker.name}${priceText}`;
|
|
81
83
|
body.appendChild(label);
|
|
82
84
|
}
|
|
83
85
|
container.appendChild(body);
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/components/InteractiveMap.tsx","../src/components/Map.tsx","../src/utils/createCustomMarkerElement.ts","../src/utils/createClusterElement.ts","../src/components/StoreCard.tsx"],"sourcesContent":["\r\nimport './styles.css';\r\n\r\n\r\nexport { InteractiveMap as NeshanMap } from './components/InteractiveMap';\r\n\r\n\r\nexport { default as RawMap } from './components/Map';\r\n\r\n\r\nexport type {\r\n MapProps,\r\n MapOptions,\r\n MarkerData,\r\n Store,\r\n MapboxMap,\r\n LngLatLike,\r\n} from './types';","\"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 \r\n const container = document.createElement('div');\r\n container.className = `neshan-marker-container ${isSelected ? 'neshan-marker-selected' : ''}`;\r\n\r\n \r\n const body = document.createElement('div');\r\n body.className = 'neshan-marker-body';\r\n\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 \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.price || marker.name) {\r\n const label = document.createElement('div');\r\n \r\n label.className = 'neshan-marker-label';\r\n \r\n label.textContent = marker.price ? `${marker.price} ت` : (marker.name ?? '');\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":["import_react","Supercluster","import_jsx_runtime","import_jsx_runtime"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/components/InteractiveMap.tsx","../src/components/Map.tsx","../src/utils/createCustomMarkerElement.ts","../src/utils/createClusterElement.ts","../src/components/StoreCard.tsx"],"sourcesContent":["\r\nimport './styles.css';\r\n\r\n\r\nexport { InteractiveMap as NeshanMap } from './components/InteractiveMap';\r\n\r\n\r\nexport { default as RawMap } from './components/Map';\r\n\r\n\r\nexport type {\r\n MapProps,\r\n MapOptions,\r\n MarkerData,\r\n Store,\r\n MapboxMap,\r\n LngLatLike,\r\n} from './types';","\"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 \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 \r\n const priceText = marker.price ? ` (${marker.price} ت)` : '';\r\n label.textContent = `${marker.name}${priceText}`;\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":["import_react","Supercluster","import_jsx_runtime","import_jsx_runtime"]}
|
package/dist/index.css
CHANGED
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
}
|
|
18
18
|
.sidebar {
|
|
19
19
|
width: 350px;
|
|
20
|
+
height: 100%;
|
|
21
|
+
max-height: 100%;
|
|
20
22
|
background: #ffffff;
|
|
21
23
|
border-left: 1px solid #e5e7eb;
|
|
22
24
|
display: flex;
|
|
@@ -30,6 +32,7 @@
|
|
|
30
32
|
font-size: 18px;
|
|
31
33
|
color: #111827;
|
|
32
34
|
border-bottom: 1px solid #f3f4f6;
|
|
35
|
+
flex-shrink: 0;
|
|
33
36
|
}
|
|
34
37
|
.sidebar-list {
|
|
35
38
|
flex: 1;
|
|
@@ -212,15 +215,15 @@
|
|
|
212
215
|
transform-origin: bottom center;
|
|
213
216
|
}
|
|
214
217
|
.neshan-marker-label {
|
|
215
|
-
background: #
|
|
216
|
-
color: #
|
|
217
|
-
font-size:
|
|
218
|
-
font-weight:
|
|
219
|
-
padding: 4px
|
|
218
|
+
background: #ffffff;
|
|
219
|
+
color: #111827 !important;
|
|
220
|
+
font-size: 11px;
|
|
221
|
+
font-weight: 800;
|
|
222
|
+
padding: 4px 10px;
|
|
220
223
|
border-radius: 20px;
|
|
221
224
|
margin-top: 6px;
|
|
222
|
-
box-shadow: 0 4px 12px rgba(
|
|
223
|
-
border: 2px solid #
|
|
225
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
226
|
+
border: 2px solid #f97316;
|
|
224
227
|
white-space: nowrap;
|
|
225
228
|
pointer-events: none;
|
|
226
229
|
}
|
|
@@ -331,6 +334,7 @@
|
|
|
331
334
|
padding: 10px 14px;
|
|
332
335
|
border-radius: 16px;
|
|
333
336
|
gap: 12px;
|
|
337
|
+
max-width: 85vw;
|
|
334
338
|
}
|
|
335
339
|
.is-mobile .product-badge-logo {
|
|
336
340
|
width: 32px;
|
package/dist/index.css.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/styles.css"],"sourcesContent":["@import \"@neshan-maps-platform/mapbox-gl/dist/NeshanMapboxGl.css\";\r\n\r\n\r\n.map-layout-root, \r\n.map-layout-root *, \r\n.map-layout-root *::before, \r\n.map-layout-root *::after {\r\n box-sizing: border-box;\r\n}\r\n\r\n.map-layout-root {\r\n display: flex;\r\n width: 100%;\r\n height: 100%;\r\n direction: rtl;\r\n font-family: inherit;\r\n overflow: hidden;\r\n}\r\n\r\n\r\n.sidebar {\r\n width: 350px;\r\n background: #ffffff;\r\n border-left: 1px solid #e5e7eb;\r\n display: flex;\r\n flex-direction: column;\r\n z-index: 1000;\r\n box-shadow: 2px 0 10px rgba(0, 0, 0, 0.05);\r\n}\r\n\r\n.sidebar-header {\r\n padding: 20px;\r\n font-weight: 800;\r\n font-size: 18px;\r\n color: #111827;\r\n border-bottom: 1px solid #f3f4f6;\r\n}\r\n\r\n.sidebar-list {\r\n flex: 1;\r\n overflow-y: auto;\r\n}\r\n\r\n\r\n.store-card {\r\n padding: 16px;\r\n border-bottom: 1px solid #f3f4f6;\r\n cursor: pointer;\r\n transition: background 0.2s ease;\r\n background: #ffffff;\r\n width: 100%;\r\n display: flex;\r\n flex-direction: column;\r\n}\r\n\r\n.store-card.active {\r\n background: #f8fafc;\r\n}\r\n\r\n.store-card-header {\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: flex-start;\r\n margin-bottom: 12px;\r\n}\r\n\r\n.store-name {\r\n margin: 0;\r\n font-size: 15px;\r\n font-weight: 800;\r\n color: #111827;\r\n}\r\n\r\n.store-location-tag {\r\n display: flex;\r\n align-items: center;\r\n gap: 4px;\r\n color: #6b7280;\r\n margin-top: 4px;\r\n}\r\n\r\n.store-city {\r\n font-size: 12px;\r\n font-weight: 600;\r\n}\r\n\r\n.report-btn {\r\n display: flex;\r\n align-items: center;\r\n gap: 4px;\r\n background: #f3f4f6;\r\n border: none;\r\n padding: 4px 10px;\r\n border-radius: 8px;\r\n color: #6b7280;\r\n font-size: 11px;\r\n font-weight: 700;\r\n cursor: pointer;\r\n}\r\n\r\n\r\n.store-desc-section {\r\n display: flex;\r\n align-items: flex-start;\r\n gap: 10px;\r\n margin-bottom: 16px;\r\n width: 100%;\r\n}\r\n\r\n.store-mini-logo {\r\n width: 40px;\r\n height: 40px;\r\n border-radius: 8px;\r\n object-fit: cover;\r\n border: 1px solid #e5e7eb;\r\n flex-shrink: 0;\r\n}\r\n\r\n.store-desc {\r\n flex: 1;\r\n font-size: 12px;\r\n color: #4b5563;\r\n line-height: 1.6;\r\n margin: 0;\r\n display: -webkit-box;\r\n -webkit-line-clamp: 2;\r\n -webkit-box-orient: vertical;\r\n overflow: hidden;\r\n}\r\n\r\n\r\n.store-card-footer {\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n border-top: 1px dashed #e5e7eb;\r\n padding-top: 12px;\r\n width: 100%;\r\n}\r\n\r\n.price-container {\r\n display: flex;\r\n align-items: baseline;\r\n gap: 4px;\r\n}\r\n\r\n.price-value {\r\n font-size: 16px;\r\n font-weight: 900;\r\n color: #111827;\r\n}\r\n\r\n.price-unit {\r\n font-size: 11px;\r\n color: #6b7280;\r\n font-weight: 600;\r\n}\r\n\r\n.contact-trigger {\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n color: #1d4ed8;\r\n font-size: 13px;\r\n font-weight: 800;\r\n}\r\n\r\n.arrow-icon {\r\n transition: transform 0.3s ease;\r\n transform: rotate(180deg);\r\n}\r\n\r\n.arrow-icon.open {\r\n transform: rotate(0deg);\r\n}\r\n\r\n\r\n.action-buttons.vertical {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 10px;\r\n margin-top: 15px;\r\n width: 100%;\r\n}\r\n\r\n.btn.full-width {\r\n width: 100%;\r\n padding: 12px;\r\n border-radius: 12px;\r\n text-decoration: none;\r\n border: none;\r\n display: block;\r\n}\r\n\r\n.btn-content {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n width: 100%;\r\n font-weight: 700;\r\n font-size: 13px;\r\n}\r\n\r\n.btn-call { background: #1d4ed8; color: white; }\r\n.btn-wa { background: #10b981; color: white; }\r\n.btn-route { background: #7c3aed; color: white; }\r\n\r\n\r\n.map-container { flex: 1; position: relative; background: #f3f4f6; }\r\n.store-details-wrapper { \r\n display: grid; \r\n grid-template-rows: 0fr; \r\n transition: grid-template-rows 0.3s ease; \r\n overflow: hidden;\r\n width: 100%;\r\n}\r\n.active .store-details-wrapper { grid-template-rows: 1fr; }\r\n.store-details-content { min-height: 0; width: 100%; }\r\n\r\n\r\n.neshan-marker-container { pointer-events: auto; }\r\n.neshan-marker-body {\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n transform-origin: bottom center;\r\n}\r\n\r\n.neshan-marker-label {\r\n background: #2563eb;\r\n color: #ffffff !important;\r\n font-size: 12px;\r\n font-weight: 900;\r\n padding: 4px 12px;\r\n border-radius: 20px;\r\n margin-top: 6px;\r\n box-shadow: 0 4px 12px rgba(37, 99, 235, 0.4);\r\n border: 2px solid #ffffff;\r\n white-space: nowrap;\r\n pointer-events: none;\r\n}\r\n\r\n\r\n.neshan-cluster-marker {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n background: #ffffff;\r\n border: 3px solid #2563eb;\r\n border-radius: 50%;\r\n width: 42px;\r\n height: 42px;\r\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.25);\r\n cursor: pointer;\r\n}\r\n\r\n.neshan-cluster-count {\r\n color: #2563eb;\r\n font-weight: 900;\r\n font-size: 16px;\r\n}\r\n\r\n\r\n.neshan-marker-selected .neshan-marker-body {\r\n animation: marker-bounce 0.8s infinite alternate cubic-bezier(0.45, 0.05, 0.55, 0.95);\r\n z-index: 100;\r\n}\r\n\r\n@keyframes marker-bounce {\r\n from { transform: translateY(0) scale(1.1); }\r\n to { transform: translateY(-12px) scale(1.15); }\r\n}\r\n\r\n\r\n.is-mobile .mobile-slider {\r\n position: absolute; bottom: 0; left: 0; right: 0;\r\n display: flex; gap: 12px; padding: 20px 20px 40px 20px;\r\n overflow-x: auto; scroll-snap-type: x mandatory;\r\n z-index: 20; align-items: flex-end;\r\n scrollbar-width: none;\r\n}\r\n.is-mobile .mobile-slider::-webkit-scrollbar { display: none; }\r\n.is-mobile .store-card {\r\n min-width: 290px; \r\n max-width: calc(100vw - 40px);\r\n border-radius: 20px; \r\n box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);\r\n scroll-snap-align: center; \r\n border: none;\r\n}\r\n\r\n\r\n.product-badge {\r\n position: absolute;\r\n bottom: 24px;\r\n right: 24px;\r\n background: rgba(255, 255, 255, 0.98);\r\n backdrop-filter: blur(12px);\r\n padding: 12px 20px;\r\n border-radius: 20px;\r\n box-shadow: 0 10px 30px rgba(0, 0, 0, 0.12);\r\n display: flex;\r\n align-items: center;\r\n gap: 16px;\r\n z-index: 40;\r\n border: 1px solid rgba(0, 0, 0, 0.05);\r\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\r\n}\r\n\r\n.product-badge-left-side {\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n gap: 4px;\r\n}\r\n\r\n.product-badge-logo {\r\n width: 65px;\r\n height: 50px;\r\n object-fit: contain;\r\n\r\n border-radius: 12px;\r\n padding: 1px;\r\n \r\n}\r\n\r\n.product-badge-fixed-label {\r\n font-size: 9px;\r\n font-weight: 800;\r\n color: #64748b;\r\n white-space: nowrap;\r\n}\r\n\r\n.product-badge-separator {\r\n width: 1px;\r\n height: 35px;\r\n background: #e2e8f0;\r\n}\r\n\r\n.product-badge-text {\r\n font-weight: 850;\r\n font-size: 12px;\r\n color: #0f172a;\r\n white-space: nowrap;\r\n}\r\n\r\n.is-mobile .product-badge {\r\n top: 16px;\r\n bottom: auto;\r\n right: 16px;\r\n padding: 10px 14px;\r\n border-radius: 16px;\r\n gap: 12px;\r\n}\r\n\r\n.is-mobile .product-badge-logo { width: 32px; height: 32px; }\r\n.is-mobile .product-badge-fixed-label { font-size: 8px; }\r\n.is-mobile .product-badge-separator { height: 25px; }\r\n.is-mobile .product-badge-text { font-size: 13px; }"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/styles.css"],"sourcesContent":["@import \"@neshan-maps-platform/mapbox-gl/dist/NeshanMapboxGl.css\";\r\n\r\n.map-layout-root, \r\n.map-layout-root *, \r\n.map-layout-root *::before, \r\n.map-layout-root *::after {\r\n box-sizing: border-box;\r\n}\r\n\r\n.map-layout-root {\r\n display: flex;\r\n width: 100%;\r\n height: 100%;\r\n direction: rtl;\r\n font-family: inherit;\r\n overflow: hidden;\r\n}\r\n\r\n/* سایدبار دسکتاپ */\r\n.sidebar {\r\n width: 350px;\r\n height: 100%; /* ✅ اضافه شد برای فیکس شدن ارتفاع */\r\n max-height: 100%; /* ✅ اضافه شد برای فعال شدن اسکرول */\r\n background: #ffffff;\r\n border-left: 1px solid #e5e7eb;\r\n display: flex;\r\n flex-direction: column;\r\n z-index: 1000;\r\n box-shadow: 2px 0 10px rgba(0, 0, 0, 0.05);\r\n}\r\n\r\n.sidebar-header {\r\n padding: 20px;\r\n font-weight: 800;\r\n font-size: 18px;\r\n color: #111827;\r\n border-bottom: 1px solid #f3f4f6;\r\n flex-shrink: 0; /* جلوگیری از فشرده شدن هدر */\r\n}\r\n\r\n.sidebar-list {\r\n flex: 1;\r\n overflow-y: auto; /* اسکرول داخلی لیست */\r\n}\r\n\r\n/* کارتهای فروشگاه */\r\n.store-card {\r\n padding: 16px;\r\n border-bottom: 1px solid #f3f4f6;\r\n cursor: pointer;\r\n transition: background 0.2s ease;\r\n background: #ffffff;\r\n width: 100%;\r\n display: flex;\r\n flex-direction: column;\r\n}\r\n\r\n.store-card.active {\r\n background: #f8fafc;\r\n}\r\n\r\n.store-card-header {\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: flex-start;\r\n margin-bottom: 12px;\r\n}\r\n\r\n.store-name {\r\n margin: 0;\r\n font-size: 15px;\r\n font-weight: 800;\r\n color: #111827;\r\n}\r\n\r\n.store-location-tag {\r\n display: flex;\r\n align-items: center;\r\n gap: 4px;\r\n color: #6b7280;\r\n margin-top: 4px;\r\n}\r\n\r\n.store-city {\r\n font-size: 12px;\r\n font-weight: 600;\r\n}\r\n\r\n.report-btn {\r\n display: flex;\r\n align-items: center;\r\n gap: 4px;\r\n background: #f3f4f6;\r\n border: none;\r\n padding: 4px 10px;\r\n border-radius: 8px;\r\n color: #6b7280;\r\n font-size: 11px;\r\n font-weight: 700;\r\n cursor: pointer;\r\n}\r\n\r\n.store-desc-section {\r\n display: flex;\r\n align-items: flex-start;\r\n gap: 10px;\r\n margin-bottom: 16px;\r\n width: 100%;\r\n}\r\n\r\n.store-mini-logo {\r\n width: 40px;\r\n height: 40px;\r\n border-radius: 8px;\r\n object-fit: cover;\r\n border: 1px solid #e5e7eb;\r\n flex-shrink: 0;\r\n}\r\n\r\n.store-desc {\r\n flex: 1;\r\n font-size: 12px;\r\n color: #4b5563;\r\n line-height: 1.6;\r\n margin: 0;\r\n display: -webkit-box;\r\n -webkit-line-clamp: 2;\r\n -webkit-box-orient: vertical;\r\n overflow: hidden;\r\n}\r\n\r\n.store-card-footer {\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n border-top: 1px dashed #e5e7eb;\r\n padding-top: 12px;\r\n width: 100%;\r\n}\r\n\r\n.price-container {\r\n display: flex;\r\n align-items: baseline;\r\n gap: 4px;\r\n}\r\n\r\n.price-value {\r\n font-size: 16px;\r\n font-weight: 900;\r\n color: #111827;\r\n}\r\n\r\n.price-unit {\r\n font-size: 11px;\r\n color: #6b7280;\r\n font-weight: 600;\r\n}\r\n\r\n.contact-trigger {\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n color: #1d4ed8;\r\n font-size: 13px;\r\n font-weight: 800;\r\n}\r\n\r\n.arrow-icon {\r\n transition: transform 0.3s ease;\r\n transform: rotate(180deg);\r\n}\r\n\r\n.arrow-icon.open {\r\n transform: rotate(0deg);\r\n}\r\n\r\n.action-buttons.vertical {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 10px;\r\n margin-top: 15px;\r\n width: 100%;\r\n}\r\n\r\n.btn.full-width {\r\n width: 100%;\r\n padding: 12px;\r\n border-radius: 12px;\r\n text-decoration: none;\r\n border: none;\r\n display: block;\r\n}\r\n\r\n.btn-content {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n width: 100%;\r\n font-weight: 700;\r\n font-size: 13px;\r\n}\r\n\r\n.btn-call { background: #1d4ed8; color: white; }\r\n.btn-wa { background: #10b981; color: white; }\r\n.btn-route { background: #7c3aed; color: white; }\r\n\r\n.map-container { flex: 1; position: relative; background: #f3f4f6; }\r\n.store-details-wrapper { \r\n display: grid; \r\n grid-template-rows: 0fr; \r\n transition: grid-template-rows 0.3s ease; \r\n overflow: hidden;\r\n width: 100%;\r\n}\r\n.active .store-details-wrapper { grid-template-rows: 1fr; }\r\n.store-details-content { min-height: 0; width: 100%; }\r\n\r\n.neshan-marker-container { pointer-events: auto; }\r\n.neshan-marker-body {\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n transform-origin: bottom center;\r\n}\r\n\r\n/* ✅ استایل اصلاح شده قیمت و نام مارکر (نارنجی-سفید) */\r\n.neshan-marker-label {\r\n background: #ffffff; /* پسزمینه سفید */\r\n color: #111827 !important; /* متن مشکی تیره */\r\n font-size: 11px;\r\n font-weight: 800;\r\n padding: 4px 10px;\r\n border-radius: 20px;\r\n margin-top: 6px;\r\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\r\n border: 2px solid #f97316; /* بردر نارنجی (orange-500) */\r\n white-space: nowrap;\r\n pointer-events: none;\r\n}\r\n\r\n.neshan-cluster-marker {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n background: #ffffff;\r\n border: 3px solid #2563eb;\r\n border-radius: 50%;\r\n width: 42px;\r\n height: 42px;\r\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.25);\r\n cursor: pointer;\r\n}\r\n\r\n.neshan-cluster-count {\r\n color: #2563eb;\r\n font-weight: 900;\r\n font-size: 16px;\r\n}\r\n\r\n.neshan-marker-selected .neshan-marker-body {\r\n animation: marker-bounce 0.8s infinite alternate cubic-bezier(0.45, 0.05, 0.55, 0.95);\r\n z-index: 100;\r\n}\r\n\r\n@keyframes marker-bounce {\r\n from { transform: translateY(0) scale(1.1); }\r\n to { transform: translateY(-12px) scale(1.15); }\r\n}\r\n\r\n.is-mobile .mobile-slider {\r\n position: absolute; bottom: 0; left: 0; right: 0;\r\n display: flex; gap: 12px; padding: 20px 20px 40px 20px;\r\n overflow-x: auto; scroll-snap-type: x mandatory;\r\n z-index: 20; align-items: flex-end;\r\n scrollbar-width: none;\r\n}\r\n.is-mobile .mobile-slider::-webkit-scrollbar { display: none; }\r\n.is-mobile .store-card {\r\n min-width: 290px; \r\n max-width: calc(100vw - 40px);\r\n border-radius: 20px; \r\n box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);\r\n scroll-snap-align: center; \r\n border: none;\r\n}\r\n\r\n.product-badge {\r\n position: absolute;\r\n bottom: 24px;\r\n right: 24px;\r\n background: rgba(255, 255, 255, 0.98);\r\n backdrop-filter: blur(12px);\r\n padding: 12px 20px;\r\n border-radius: 20px;\r\n box-shadow: 0 10px 30px rgba(0, 0, 0, 0.12);\r\n display: flex;\r\n align-items: center;\r\n gap: 16px;\r\n z-index: 40;\r\n border: 1px solid rgba(0, 0, 0, 0.05);\r\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\r\n}\r\n\r\n.product-badge-left-side {\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n gap: 4px;\r\n}\r\n\r\n.product-badge-logo {\r\n width: 65px;\r\n height: 50px;\r\n object-fit: contain;\r\n border-radius: 12px;\r\n padding: 1px;\r\n}\r\n\r\n.product-badge-fixed-label {\r\n font-size: 9px;\r\n font-weight: 800;\r\n color: #64748b;\r\n white-space: nowrap;\r\n}\r\n\r\n.product-badge-separator {\r\n width: 1px;\r\n height: 35px;\r\n background: #e2e8f0;\r\n}\r\n\r\n.product-badge-text {\r\n font-weight: 850;\r\n font-size: 12px;\r\n color: #0f172a;\r\n white-space: nowrap;\r\n}\r\n\r\n.is-mobile .product-badge {\r\n top: 16px;\r\n bottom: auto;\r\n right: 16px;\r\n padding: 10px 14px;\r\n border-radius: 16px;\r\n gap: 12px;\r\n max-width: 85vw;\r\n}\r\n\r\n.is-mobile .product-badge-logo { width: 32px; height: 32px; }\r\n.is-mobile .product-badge-fixed-label { font-size: 8px; }\r\n.is-mobile .product-badge-separator { height: 25px; }\r\n.is-mobile .product-badge-text { font-size: 13px; }"],"mappings":"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","names":[]}
|
package/dist/index.js
CHANGED
|
@@ -19,6 +19,7 @@ function createCustomMarkerElement({
|
|
|
19
19
|
width: "40px",
|
|
20
20
|
height: "40px",
|
|
21
21
|
borderRadius: "50%",
|
|
22
|
+
backgroundColor: "white",
|
|
22
23
|
border: isSelected ? "3px solid #2563eb" : "",
|
|
23
24
|
boxShadow: "0 4px 8px rgba(0,0,0,0.2)",
|
|
24
25
|
display: "flex",
|
|
@@ -37,10 +38,11 @@ function createCustomMarkerElement({
|
|
|
37
38
|
iconBox.style.backgroundColor = isSelected ? "#2563eb" : "#ef4444";
|
|
38
39
|
}
|
|
39
40
|
body.appendChild(iconBox);
|
|
40
|
-
if (marker.
|
|
41
|
+
if (marker.name || marker.price) {
|
|
41
42
|
const label = document.createElement("div");
|
|
42
43
|
label.className = "neshan-marker-label";
|
|
43
|
-
|
|
44
|
+
const priceText = marker.price ? ` (${marker.price} \u062A)` : "";
|
|
45
|
+
label.textContent = `${marker.name}${priceText}`;
|
|
44
46
|
body.appendChild(label);
|
|
45
47
|
}
|
|
46
48
|
container.appendChild(body);
|
package/dist/index.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 \r\n const container = document.createElement('div');\r\n container.className = `neshan-marker-container ${isSelected ? 'neshan-marker-selected' : ''}`;\r\n\r\n \r\n const body = document.createElement('div');\r\n body.className = 'neshan-marker-body';\r\n\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 \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.price || marker.name) {\r\n const label = document.createElement('div');\r\n \r\n label.className = 'neshan-marker-label';\r\n \r\n label.textContent = marker.price ? `${marker.price} ت` : (marker.name ?? '');\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 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 \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 \r\n const priceText = marker.price ? ` (${marker.price} ت)` : '';\r\n label.textContent = `${marker.name}${priceText}`;\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/react.cjs
CHANGED
|
@@ -56,6 +56,7 @@ function createCustomMarkerElement({
|
|
|
56
56
|
width: "40px",
|
|
57
57
|
height: "40px",
|
|
58
58
|
borderRadius: "50%",
|
|
59
|
+
backgroundColor: "white",
|
|
59
60
|
border: isSelected ? "3px solid #2563eb" : "",
|
|
60
61
|
boxShadow: "0 4px 8px rgba(0,0,0,0.2)",
|
|
61
62
|
display: "flex",
|
|
@@ -74,10 +75,11 @@ function createCustomMarkerElement({
|
|
|
74
75
|
iconBox.style.backgroundColor = isSelected ? "#2563eb" : "#ef4444";
|
|
75
76
|
}
|
|
76
77
|
body.appendChild(iconBox);
|
|
77
|
-
if (marker.
|
|
78
|
+
if (marker.name || marker.price) {
|
|
78
79
|
const label = document.createElement("div");
|
|
79
80
|
label.className = "neshan-marker-label";
|
|
80
|
-
|
|
81
|
+
const priceText = marker.price ? ` (${marker.price} \u062A)` : "";
|
|
82
|
+
label.textContent = `${marker.name}${priceText}`;
|
|
81
83
|
body.appendChild(label);
|
|
82
84
|
}
|
|
83
85
|
container.appendChild(body);
|
package/dist/react.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/react.ts","../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\n\r\nexport { InteractiveMap as NeshanMap } from './components/InteractiveMap';\r\n\r\n\r\nexport type {\r\n MapProps,\r\n MapOptions,\r\n MarkerData,\r\n Store,\r\n} from './types';","\"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 \r\n const container = document.createElement('div');\r\n container.className = `neshan-marker-container ${isSelected ? 'neshan-marker-selected' : ''}`;\r\n\r\n \r\n const body = document.createElement('div');\r\n body.className = 'neshan-marker-body';\r\n\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 \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.price || marker.name) {\r\n const label = document.createElement('div');\r\n \r\n label.className = 'neshan-marker-label';\r\n \r\n label.textContent = marker.price ? `${marker.price} ت` : (marker.name ?? '');\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":["import_react","Supercluster","import_jsx_runtime","import_jsx_runtime"]}
|
|
1
|
+
{"version":3,"sources":["../src/react.ts","../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\n\r\nexport { InteractiveMap as NeshanMap } from './components/InteractiveMap';\r\n\r\n\r\nexport type {\r\n MapProps,\r\n MapOptions,\r\n MarkerData,\r\n Store,\r\n} from './types';","\"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 \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 \r\n const priceText = marker.price ? ` (${marker.price} ت)` : '';\r\n label.textContent = `${marker.name}${priceText}`;\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":["import_react","Supercluster","import_jsx_runtime","import_jsx_runtime"]}
|
package/dist/react.js
CHANGED
|
@@ -21,6 +21,7 @@ function createCustomMarkerElement({
|
|
|
21
21
|
width: "40px",
|
|
22
22
|
height: "40px",
|
|
23
23
|
borderRadius: "50%",
|
|
24
|
+
backgroundColor: "white",
|
|
24
25
|
border: isSelected ? "3px solid #2563eb" : "",
|
|
25
26
|
boxShadow: "0 4px 8px rgba(0,0,0,0.2)",
|
|
26
27
|
display: "flex",
|
|
@@ -39,10 +40,11 @@ function createCustomMarkerElement({
|
|
|
39
40
|
iconBox.style.backgroundColor = isSelected ? "#2563eb" : "#ef4444";
|
|
40
41
|
}
|
|
41
42
|
body.appendChild(iconBox);
|
|
42
|
-
if (marker.
|
|
43
|
+
if (marker.name || marker.price) {
|
|
43
44
|
const label = document.createElement("div");
|
|
44
45
|
label.className = "neshan-marker-label";
|
|
45
|
-
|
|
46
|
+
const priceText = marker.price ? ` (${marker.price} \u062A)` : "";
|
|
47
|
+
label.textContent = `${marker.name}${priceText}`;
|
|
46
48
|
body.appendChild(label);
|
|
47
49
|
}
|
|
48
50
|
container.appendChild(body);
|
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 \r\n const container = document.createElement('div');\r\n container.className = `neshan-marker-container ${isSelected ? 'neshan-marker-selected' : ''}`;\r\n\r\n \r\n const body = document.createElement('div');\r\n body.className = 'neshan-marker-body';\r\n\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 \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.price || marker.name) {\r\n const label = document.createElement('div');\r\n \r\n label.className = 'neshan-marker-label';\r\n \r\n label.textContent = marker.price ? `${marker.price} ت` : (marker.name ?? '');\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 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 \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 \r\n const priceText = marker.price ? ` (${marker.price} ت)` : '';\r\n label.textContent = `${marker.name}${priceText}`;\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,5 @@
|
|
|
1
1
|
@import "@neshan-maps-platform/mapbox-gl/dist/NeshanMapboxGl.css";
|
|
2
2
|
|
|
3
|
-
|
|
4
3
|
.map-layout-root,
|
|
5
4
|
.map-layout-root *,
|
|
6
5
|
.map-layout-root *::before,
|
|
@@ -17,9 +16,11 @@
|
|
|
17
16
|
overflow: hidden;
|
|
18
17
|
}
|
|
19
18
|
|
|
20
|
-
|
|
19
|
+
/* سایدبار دسکتاپ */
|
|
21
20
|
.sidebar {
|
|
22
21
|
width: 350px;
|
|
22
|
+
height: 100%; /* ✅ اضافه شد برای فیکس شدن ارتفاع */
|
|
23
|
+
max-height: 100%; /* ✅ اضافه شد برای فعال شدن اسکرول */
|
|
23
24
|
background: #ffffff;
|
|
24
25
|
border-left: 1px solid #e5e7eb;
|
|
25
26
|
display: flex;
|
|
@@ -34,14 +35,15 @@
|
|
|
34
35
|
font-size: 18px;
|
|
35
36
|
color: #111827;
|
|
36
37
|
border-bottom: 1px solid #f3f4f6;
|
|
38
|
+
flex-shrink: 0; /* جلوگیری از فشرده شدن هدر */
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
.sidebar-list {
|
|
40
42
|
flex: 1;
|
|
41
|
-
overflow-y: auto;
|
|
43
|
+
overflow-y: auto; /* اسکرول داخلی لیست */
|
|
42
44
|
}
|
|
43
45
|
|
|
44
|
-
|
|
46
|
+
/* کارتهای فروشگاه */
|
|
45
47
|
.store-card {
|
|
46
48
|
padding: 16px;
|
|
47
49
|
border-bottom: 1px solid #f3f4f6;
|
|
@@ -98,7 +100,6 @@
|
|
|
98
100
|
cursor: pointer;
|
|
99
101
|
}
|
|
100
102
|
|
|
101
|
-
|
|
102
103
|
.store-desc-section {
|
|
103
104
|
display: flex;
|
|
104
105
|
align-items: flex-start;
|
|
@@ -128,7 +129,6 @@
|
|
|
128
129
|
overflow: hidden;
|
|
129
130
|
}
|
|
130
131
|
|
|
131
|
-
|
|
132
132
|
.store-card-footer {
|
|
133
133
|
display: flex;
|
|
134
134
|
justify-content: space-between;
|
|
@@ -174,7 +174,6 @@
|
|
|
174
174
|
transform: rotate(0deg);
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
-
|
|
178
177
|
.action-buttons.vertical {
|
|
179
178
|
display: flex;
|
|
180
179
|
flex-direction: column;
|
|
@@ -205,7 +204,6 @@
|
|
|
205
204
|
.btn-wa { background: #10b981; color: white; }
|
|
206
205
|
.btn-route { background: #7c3aed; color: white; }
|
|
207
206
|
|
|
208
|
-
|
|
209
207
|
.map-container { flex: 1; position: relative; background: #f3f4f6; }
|
|
210
208
|
.store-details-wrapper {
|
|
211
209
|
display: grid;
|
|
@@ -217,7 +215,6 @@
|
|
|
217
215
|
.active .store-details-wrapper { grid-template-rows: 1fr; }
|
|
218
216
|
.store-details-content { min-height: 0; width: 100%; }
|
|
219
217
|
|
|
220
|
-
|
|
221
218
|
.neshan-marker-container { pointer-events: auto; }
|
|
222
219
|
.neshan-marker-body {
|
|
223
220
|
display: flex;
|
|
@@ -226,21 +223,21 @@
|
|
|
226
223
|
transform-origin: bottom center;
|
|
227
224
|
}
|
|
228
225
|
|
|
226
|
+
/* ✅ استایل اصلاح شده قیمت و نام مارکر (نارنجی-سفید) */
|
|
229
227
|
.neshan-marker-label {
|
|
230
|
-
background: #
|
|
231
|
-
color: #
|
|
232
|
-
font-size:
|
|
233
|
-
font-weight:
|
|
234
|
-
padding: 4px
|
|
228
|
+
background: #ffffff; /* پسزمینه سفید */
|
|
229
|
+
color: #111827 !important; /* متن مشکی تیره */
|
|
230
|
+
font-size: 11px;
|
|
231
|
+
font-weight: 800;
|
|
232
|
+
padding: 4px 10px;
|
|
235
233
|
border-radius: 20px;
|
|
236
234
|
margin-top: 6px;
|
|
237
|
-
box-shadow: 0 4px 12px rgba(
|
|
238
|
-
border: 2px solid #
|
|
235
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
236
|
+
border: 2px solid #f97316; /* بردر نارنجی (orange-500) */
|
|
239
237
|
white-space: nowrap;
|
|
240
238
|
pointer-events: none;
|
|
241
239
|
}
|
|
242
240
|
|
|
243
|
-
|
|
244
241
|
.neshan-cluster-marker {
|
|
245
242
|
display: flex;
|
|
246
243
|
align-items: center;
|
|
@@ -260,7 +257,6 @@
|
|
|
260
257
|
font-size: 16px;
|
|
261
258
|
}
|
|
262
259
|
|
|
263
|
-
|
|
264
260
|
.neshan-marker-selected .neshan-marker-body {
|
|
265
261
|
animation: marker-bounce 0.8s infinite alternate cubic-bezier(0.45, 0.05, 0.55, 0.95);
|
|
266
262
|
z-index: 100;
|
|
@@ -271,7 +267,6 @@
|
|
|
271
267
|
to { transform: translateY(-12px) scale(1.15); }
|
|
272
268
|
}
|
|
273
269
|
|
|
274
|
-
|
|
275
270
|
.is-mobile .mobile-slider {
|
|
276
271
|
position: absolute; bottom: 0; left: 0; right: 0;
|
|
277
272
|
display: flex; gap: 12px; padding: 20px 20px 40px 20px;
|
|
@@ -289,7 +284,6 @@
|
|
|
289
284
|
border: none;
|
|
290
285
|
}
|
|
291
286
|
|
|
292
|
-
|
|
293
287
|
.product-badge {
|
|
294
288
|
position: absolute;
|
|
295
289
|
bottom: 24px;
|
|
@@ -318,10 +312,8 @@
|
|
|
318
312
|
width: 65px;
|
|
319
313
|
height: 50px;
|
|
320
314
|
object-fit: contain;
|
|
321
|
-
|
|
322
315
|
border-radius: 12px;
|
|
323
316
|
padding: 1px;
|
|
324
|
-
|
|
325
317
|
}
|
|
326
318
|
|
|
327
319
|
.product-badge-fixed-label {
|
|
@@ -351,6 +343,7 @@
|
|
|
351
343
|
padding: 10px 14px;
|
|
352
344
|
border-radius: 16px;
|
|
353
345
|
gap: 12px;
|
|
346
|
+
max-width: 85vw;
|
|
354
347
|
}
|
|
355
348
|
|
|
356
349
|
.is-mobile .product-badge-logo { width: 32px; height: 32px; }
|