mehdi-akbari-map 0.1.2 → 1.0.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/components/Map.d.ts.map +1 -1
- package/dist/index.cjs +74 -49
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +124 -38
- package/dist/index.css.map +1 -1
- package/dist/index.js +75 -50
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +74 -49
- package/dist/react.cjs.map +1 -1
- package/dist/react.js +75 -50
- package/dist/react.js.map +1 -1
- package/dist/styles.css +168 -41
- package/dist/utils/createClusterElement.d.ts +2 -0
- package/dist/utils/createClusterElement.d.ts.map +1 -0
- package/package.json +4 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Map.d.ts","sourceRoot":"","sources":["../../src/components/Map.tsx"],"names":[],"mappings":"AAEA,OAAO,
|
|
1
|
+
{"version":3,"file":"Map.d.ts","sourceRoot":"","sources":["../../src/components/Map.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAmD,MAAM,OAAO,CAAC;AACxE,OAAO,KAAK,EAAoB,QAAQ,EAAE,MAAM,UAAU,CAAC;AAO3D,QAAA,MAAM,GAAG,EAAE,KAAK,CAAC,EAAE,CAAC,QAAQ,CAkI3B,CAAC;AAEF,eAAe,GAAG,CAAC"}
|
package/dist/index.cjs
CHANGED
|
@@ -93,7 +93,16 @@ function createCustomMarkerElement({
|
|
|
93
93
|
return container;
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
// src/utils/createClusterElement.ts
|
|
97
|
+
function createClusterElement(count) {
|
|
98
|
+
const el = document.createElement("div");
|
|
99
|
+
el.className = "neshan-cluster-marker";
|
|
100
|
+
el.innerHTML = `<span class="neshan-cluster-count">${count}</span>`;
|
|
101
|
+
return el;
|
|
102
|
+
}
|
|
103
|
+
|
|
96
104
|
// src/components/Map.tsx
|
|
105
|
+
var import_supercluster = __toESM(require("supercluster"), 1);
|
|
97
106
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
98
107
|
var Map = ({
|
|
99
108
|
options,
|
|
@@ -102,23 +111,20 @@ var Map = ({
|
|
|
102
111
|
selectedMarkerId = null,
|
|
103
112
|
onMarkerClick,
|
|
104
113
|
onMapLoad,
|
|
105
|
-
className = "",
|
|
106
114
|
style = { width: "100%", height: "100%" }
|
|
107
115
|
}) => {
|
|
108
116
|
const mapContainerRef = (0, import_react.useRef)(null);
|
|
109
117
|
const [mapInstance, setMapInstance] = (0, import_react.useState)(null);
|
|
110
118
|
const [mapLib, setMapLib] = (0, import_react.useState)(null);
|
|
111
119
|
const markersRef = (0, import_react.useRef)([]);
|
|
120
|
+
const clusterIndex = (0, import_react.useRef)(new import_supercluster.default({
|
|
121
|
+
radius: 60,
|
|
122
|
+
maxZoom: 16
|
|
123
|
+
}));
|
|
112
124
|
(0, import_react.useEffect)(() => {
|
|
113
|
-
let isMounted = true;
|
|
114
125
|
import("@neshan-maps-platform/mapbox-gl").then((mod) => {
|
|
115
|
-
|
|
116
|
-
setMapLib(mod.default || mod);
|
|
117
|
-
}
|
|
126
|
+
setMapLib(mod.default || mod);
|
|
118
127
|
});
|
|
119
|
-
return () => {
|
|
120
|
-
isMounted = false;
|
|
121
|
-
};
|
|
122
128
|
}, []);
|
|
123
129
|
(0, import_react.useEffect)(() => {
|
|
124
130
|
if (!mapLib || !mapContainerRef.current || mapInstance) return;
|
|
@@ -131,56 +137,75 @@ var Map = ({
|
|
|
131
137
|
onMapLoad?.(map);
|
|
132
138
|
});
|
|
133
139
|
return () => {
|
|
134
|
-
if (map)
|
|
135
|
-
map.remove();
|
|
136
|
-
setMapInstance(null);
|
|
137
|
-
}
|
|
140
|
+
if (map) map.remove();
|
|
138
141
|
};
|
|
139
142
|
}, [mapLib]);
|
|
140
143
|
(0, import_react.useEffect)(() => {
|
|
144
|
+
if (!mapInstance || !selectedMarkerId) return;
|
|
145
|
+
const selectedStore = markers.find((m) => m.id === selectedMarkerId);
|
|
146
|
+
if (selectedStore) {
|
|
147
|
+
mapInstance.flyTo({
|
|
148
|
+
center: [selectedStore.lng, selectedStore.lat],
|
|
149
|
+
zoom: 16,
|
|
150
|
+
// زوم نزدیک هنگام انتخاب
|
|
151
|
+
essential: true,
|
|
152
|
+
duration: 1500
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}, [selectedMarkerId, mapInstance, markers]);
|
|
156
|
+
const updateMarkers = (0, import_react.useCallback)(() => {
|
|
141
157
|
if (!mapInstance || !mapLib) return;
|
|
158
|
+
const bounds = mapInstance.getBounds();
|
|
159
|
+
const zoom = Math.floor(mapInstance.getZoom());
|
|
160
|
+
const clusters = clusterIndex.current.getClusters(
|
|
161
|
+
[bounds.getWest(), bounds.getSouth(), bounds.getEast(), bounds.getNorth()],
|
|
162
|
+
zoom
|
|
163
|
+
);
|
|
142
164
|
markersRef.current.forEach((m) => m.remove());
|
|
143
165
|
markersRef.current = [];
|
|
144
|
-
|
|
145
|
-
const
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
166
|
+
clusters.forEach((cluster) => {
|
|
167
|
+
const [lng, lat] = cluster.geometry.coordinates;
|
|
168
|
+
const { cluster: isCluster, point_count: count } = cluster.properties;
|
|
169
|
+
let el;
|
|
170
|
+
if (isCluster) {
|
|
171
|
+
el = createClusterElement(count);
|
|
172
|
+
el.onclick = () => {
|
|
173
|
+
const expansionZoom = Math.min(
|
|
174
|
+
clusterIndex.current.getClusterExpansionZoom(cluster.id),
|
|
175
|
+
18
|
|
176
|
+
);
|
|
177
|
+
mapInstance.easeTo({ center: [lng, lat], zoom: expansionZoom });
|
|
178
|
+
};
|
|
179
|
+
} else {
|
|
180
|
+
const storeData = cluster.properties;
|
|
181
|
+
el = createCustomMarkerElement({
|
|
182
|
+
marker: storeData,
|
|
183
|
+
isSelected: storeData.id === selectedMarkerId,
|
|
184
|
+
logoSrc: markerLogoUrl
|
|
185
|
+
});
|
|
186
|
+
el.onclick = () => onMarkerClick?.(storeData, 0, mapInstance);
|
|
187
|
+
}
|
|
188
|
+
const newMarker = new mapLib.Marker({ element: el, anchor: isCluster ? "center" : "bottom" }).setLngLat([lng, lat]).addTo(mapInstance);
|
|
189
|
+
markersRef.current.push(newMarker);
|
|
161
190
|
});
|
|
191
|
+
}, [mapInstance, mapLib, selectedMarkerId, markerLogoUrl, onMarkerClick]);
|
|
192
|
+
(0, import_react.useEffect)(() => {
|
|
193
|
+
const points = markers.map((s) => ({
|
|
194
|
+
type: "Feature",
|
|
195
|
+
properties: { ...s },
|
|
196
|
+
geometry: { type: "Point", coordinates: [s.lng, s.lat] }
|
|
197
|
+
}));
|
|
198
|
+
clusterIndex.current.load(points);
|
|
199
|
+
updateMarkers();
|
|
200
|
+
}, [markers, updateMarkers]);
|
|
201
|
+
(0, import_react.useEffect)(() => {
|
|
202
|
+
if (!mapInstance) return;
|
|
203
|
+
mapInstance.on("moveend", updateMarkers);
|
|
162
204
|
return () => {
|
|
163
|
-
|
|
205
|
+
mapInstance.off("moveend", updateMarkers);
|
|
164
206
|
};
|
|
165
|
-
}, [
|
|
166
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
167
|
-
"div",
|
|
168
|
-
{
|
|
169
|
-
style: {
|
|
170
|
-
position: "relative",
|
|
171
|
-
overflow: "hidden",
|
|
172
|
-
...style
|
|
173
|
-
},
|
|
174
|
-
className: `neshan-map-wrapper ${className}`,
|
|
175
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
176
|
-
"div",
|
|
177
|
-
{
|
|
178
|
-
ref: mapContainerRef,
|
|
179
|
-
style: { width: "100%", height: "100%" }
|
|
180
|
-
}
|
|
181
|
-
)
|
|
182
|
-
}
|
|
183
|
-
);
|
|
207
|
+
}, [mapInstance, updateMarkers]);
|
|
208
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { position: "relative", ...style }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref: mapContainerRef, style: { width: "100%", height: "100%" } }) });
|
|
184
209
|
};
|
|
185
210
|
var Map_default = Map;
|
|
186
211
|
|
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/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 } 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 // ✅ ذخیره نمونه نقشه برای پاس دادن به کلیک مارکر\r\n const [mapInstance, setMapInstance] = useState<any>(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 const handleStoreSelect = (store: Store, index: number) => {\r\n // ✅ حالا دیگر null نمیفرستیم، نمونه واقعی نقشه را میفرستیم\r\n props.onMarkerClick?.(store, index, mapInstance);\r\n };\r\n\r\n // اطمینان از اینکه markers همیشه یک آرایه است\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 \r\n {!isMobile && (\r\n <aside className=\"sidebar\">\r\n <header className=\"sidebar-header\">لیست فروشگاهها</header>\r\n <div className=\"sidebar-list\">\r\n {/* ✅ استفاده از safeMarkers برای جلوگیری از خطای undefined */}\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 />\r\n ))}\r\n </div>\r\n </aside>\r\n )}\r\n\r\n <main className=\"map-container\">\r\n {/* ✅ دریافت نمونه نقشه وقتی لود شد */}\r\n <Map \r\n {...props} \r\n onMapLoad={(map) => {\r\n setMapInstance(map);\r\n props.onMapLoad?.(map);\r\n }} \r\n />\r\n \r\n {props.productName && (\r\n <div className=\"product-badge\">\r\n <span>{props.productName}</span>\r\n </div>\r\n )}\r\n\r\n {isMobile && (\r\n <div className=\"mobile-slider\">\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 />\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 } from 'react';\r\nimport type { MapboxMap, MarkerData, MapProps } from '../types';\r\nimport { createCustomMarkerElement } from '../utils/createCustomMarkerElement';\r\n\r\n\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 className = '',\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\r\n \r\n useEffect(() => {\r\n let isMounted = true;\r\n \r\n import('@neshan-maps-platform/mapbox-gl').then((mod) => {\r\n if (isMounted) {\r\n setMapLib(mod.default || mod);\r\n }\r\n });\r\n\r\n return () => {\r\n isMounted = false;\r\n };\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 setMapInstance(map);\r\n onMapLoad?.(map);\r\n });\r\n\r\n \r\n return () => {\r\n if (map) {\r\n map.remove();\r\n setMapInstance(null);\r\n }\r\n };\r\n \r\n \r\n }, [mapLib]); \r\n\r\n \r\n useEffect(() => {\r\n if (!mapInstance || !mapLib) return;\r\n\r\n \r\n markersRef.current.forEach((m) => m.remove());\r\n markersRef.current = [];\r\n\r\n markers.forEach((markerData: MarkerData, index: number) => {\r\n const isSelected = markerData.id === selectedMarkerId;\r\n \r\n \r\n const el = createCustomMarkerElement({\r\n marker: markerData,\r\n isSelected: isSelected,\r\n logoSrc: markerLogoUrl, \r\n });\r\n\r\n \r\n const marker = new mapLib.Marker({ \r\n element: el,\r\n anchor: 'bottom', \r\n offset: [0, 0] \r\n })\r\n .setLngLat([markerData.lng, markerData.lat])\r\n .addTo(mapInstance);\r\n\r\n \r\n el.addEventListener('click', (e) => {\r\n e.stopPropagation();\r\n onMarkerClick?.(markerData, index, mapInstance);\r\n });\r\n\r\n markersRef.current.push(marker);\r\n });\r\n\r\n \r\n return () => {\r\n markersRef.current.forEach((m) => m.remove());\r\n };\r\n }, [markers, mapInstance, mapLib, selectedMarkerId, markerLogoUrl, onMarkerClick]);\r\n\r\n return (\r\n <div \r\n style={{ \r\n position: 'relative', \r\n overflow: 'hidden',\r\n ...style \r\n }} \r\n className={`neshan-map-wrapper ${className}`}\r\n >\r\n <div \r\n ref={mapContainerRef} \r\n style={{ width: '100%', height: '100%' }} \r\n />\r\n </div>\r\n );\r\n};\r\n\r\nexport default Map;","import type { MarkerData } from '../types';\r\n\r\ninterface CreateMarkerOptions {\r\n marker: MarkerData;\r\n isSelected: boolean;\r\n logoSrc?: string;\r\n showPrice?: boolean;\r\n}\r\n\r\nexport function createCustomMarkerElement({\r\n marker,\r\n isSelected,\r\n logoSrc = '',\r\n showPrice = true,\r\n}: CreateMarkerOptions): HTMLElement {\r\n \r\n const container = document.createElement('div');\r\n container.className = 'neshan-marker-container';\r\n\r\n \r\n const body = document.createElement('div');\r\n body.className = `neshan-marker-body ${isSelected ? 'neshan-marker-selected' : ''}`;\r\n \r\n \r\n Object.assign(body.style, {\r\n display: 'flex',\r\n flexDirection: 'column',\r\n alignItems: 'center',\r\n transition: 'transform 0.3s ease',\r\n transform: isSelected ? 'scale(1.2)' : 'scale(1)',\r\n });\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 backgroundColor: 'white',\r\n border: isSelected ? '3px solid #3b82f6' : '2px solid #ef4444',\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 = '#ef4444';\r\n }\r\n\r\n body.appendChild(iconBox);\r\n\r\n \r\n if (showPrice && (marker.price || marker.name)) {\r\n const label = document.createElement('div');\r\n label.className = 'neshan-marker-label';\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}","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}\r\n\r\nexport const StoreCard: React.FC<StoreCardProps> = ({ store, isSelected, onSelect }) => {\r\n const googleMapUrl = `https://www.google.com/maps/dir/?api=1&destination=${store.lat},${store.lng}`;\r\n\r\n return (\r\n <div \r\n className={`store-card ${isSelected ? 'active' : ''}`}\r\n onClick={onSelect}\r\n >\r\n <div className=\"store-card-header\">\r\n <div>\r\n <h4 className=\"store-name\">{store.name}</h4>\r\n <span className=\"store-city\">{store.city}</span>\r\n </div>\r\n <div className=\"store-price\">{store.price} تومان</div>\r\n </div>\r\n\r\n <p className=\"store-desc\">{store.desc}</p>\r\n\r\n {/* بخش آکاردئونی با CSS Animation */}\r\n <div className=\"store-details-wrapper\">\r\n <div className=\"store-details-content\">\r\n <div className=\"action-buttons\">\r\n {store.phone && (\r\n <a href={`tel:${store.phone}`} className=\"btn btn-call\">\r\n <span>تماس</span>\r\n </a>\r\n )}\r\n {store.whatsapp && (\r\n <a href={`https://wa.me/${store.whatsapp}`} target=\"_blank\" className=\"btn btn-wa\">\r\n <span>واتساپ</span>\r\n </a>\r\n )}\r\n <a href={googleMapUrl} target=\"_blank\" className=\"btn btn-route\">\r\n <span>مسیریابی</span>\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","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 } 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 // ✅ ذخیره نمونه نقشه برای پاس دادن به کلیک مارکر\r\n const [mapInstance, setMapInstance] = useState<any>(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 const handleStoreSelect = (store: Store, index: number) => {\r\n // ✅ حالا دیگر null نمیفرستیم، نمونه واقعی نقشه را میفرستیم\r\n props.onMarkerClick?.(store, index, mapInstance);\r\n };\r\n\r\n // اطمینان از اینکه markers همیشه یک آرایه است\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 \r\n {!isMobile && (\r\n <aside className=\"sidebar\">\r\n <header className=\"sidebar-header\">لیست فروشگاهها</header>\r\n <div className=\"sidebar-list\">\r\n {/* ✅ استفاده از safeMarkers برای جلوگیری از خطای undefined */}\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 />\r\n ))}\r\n </div>\r\n </aside>\r\n )}\r\n\r\n <main className=\"map-container\">\r\n {/* ✅ دریافت نمونه نقشه وقتی لود شد */}\r\n <Map \r\n {...props} \r\n onMapLoad={(map) => {\r\n setMapInstance(map);\r\n props.onMapLoad?.(map);\r\n }} \r\n />\r\n \r\n {props.productName && (\r\n <div className=\"product-badge\">\r\n <span>{props.productName}</span>\r\n </div>\r\n )}\r\n\r\n {isMobile && (\r\n <div className=\"mobile-slider\">\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 />\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\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 \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 import('@neshan-maps-platform/mapbox-gl').then((mod) => {\r\n setMapLib(mod.default || mod);\r\n });\r\n }, []);\r\n\r\n // ۲. ساخت نقشه\r\n useEffect(() => {\r\n if (!mapLib || !mapContainerRef.current || mapInstance) return;\r\n const map = new mapLib.Map({\r\n container: mapContainerRef.current,\r\n ...options,\r\n });\r\n map.on('load', () => {\r\n setMapInstance(map);\r\n onMapLoad?.(map);\r\n });\r\n return () => { if (map) map.remove(); };\r\n }, [mapLib]);\r\n\r\n // ۳. منطق زوم خودکار (FlyTo) وقتی مارکری انتخاب میشود\r\n useEffect(() => {\r\n if (!mapInstance || !selectedMarkerId) 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: 1500\r\n });\r\n }\r\n }, [selectedMarkerId, mapInstance, markers]);\r\n\r\n // ۴. تابع رندر کردن مارکرها و کلاسترها\r\n const updateMarkers = useCallback(() => {\r\n if (!mapInstance || !mapLib) return;\r\n\r\n const bounds = mapInstance.getBounds();\r\n const zoom = Math.floor(mapInstance.getZoom());\r\n\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 // پاک کردن مارکرهای قبلی\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 // اگر کلاستر بود\r\n el = createClusterElement(count);\r\n el.onclick = () => {\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 // اگر مارکر تکی بود\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 }, [mapInstance, mapLib, selectedMarkerId, markerLogoUrl, onMarkerClick]);\r\n\r\n // ۵. آمادهسازی دادهها برای کلاسترینگ\r\n useEffect(() => {\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) 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', ...style }}>\r\n <div ref={mapContainerRef} style={{ width: '100%', height: '100%' }} />\r\n </div>\r\n );\r\n};\r\n\r\nexport default Map;","import type { MarkerData } from '../types';\r\n\r\ninterface CreateMarkerOptions {\r\n marker: MarkerData;\r\n isSelected: boolean;\r\n logoSrc?: string;\r\n showPrice?: boolean;\r\n}\r\n\r\nexport function createCustomMarkerElement({\r\n marker,\r\n isSelected,\r\n logoSrc = '',\r\n showPrice = true,\r\n}: CreateMarkerOptions): HTMLElement {\r\n \r\n const container = document.createElement('div');\r\n container.className = 'neshan-marker-container';\r\n\r\n \r\n const body = document.createElement('div');\r\n body.className = `neshan-marker-body ${isSelected ? 'neshan-marker-selected' : ''}`;\r\n \r\n \r\n Object.assign(body.style, {\r\n display: 'flex',\r\n flexDirection: 'column',\r\n alignItems: 'center',\r\n transition: 'transform 0.3s ease',\r\n transform: isSelected ? 'scale(1.2)' : 'scale(1)',\r\n });\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 backgroundColor: 'white',\r\n border: isSelected ? '3px solid #3b82f6' : '2px solid #ef4444',\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 = '#ef4444';\r\n }\r\n\r\n body.appendChild(iconBox);\r\n\r\n \r\n if (showPrice && (marker.price || marker.name)) {\r\n const label = document.createElement('div');\r\n label.className = 'neshan-marker-label'; // استفاده از کلاس CSS جدید\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 el = document.createElement('div');\r\n el.className = 'neshan-cluster-marker';\r\n el.innerHTML = `<span class=\"neshan-cluster-count\">${count}</span>`;\r\n return el;\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}\r\n\r\nexport const StoreCard: React.FC<StoreCardProps> = ({ store, isSelected, onSelect }) => {\r\n const googleMapUrl = `https://www.google.com/maps/dir/?api=1&destination=${store.lat},${store.lng}`;\r\n\r\n return (\r\n <div \r\n className={`store-card ${isSelected ? 'active' : ''}`}\r\n onClick={onSelect}\r\n >\r\n <div className=\"store-card-header\">\r\n <div>\r\n <h4 className=\"store-name\">{store.name}</h4>\r\n <span className=\"store-city\">{store.city}</span>\r\n </div>\r\n <div className=\"store-price\">{store.price} تومان</div>\r\n </div>\r\n\r\n <p className=\"store-desc\">{store.desc}</p>\r\n\r\n {/* بخش آکاردئونی با CSS Animation */}\r\n <div className=\"store-details-wrapper\">\r\n <div className=\"store-details-content\">\r\n <div className=\"action-buttons\">\r\n {store.phone && (\r\n <a href={`tel:${store.phone}`} className=\"btn btn-call\">\r\n <span>تماس</span>\r\n </a>\r\n )}\r\n {store.whatsapp && (\r\n <a href={`https://wa.me/${store.whatsapp}`} target=\"_blank\" className=\"btn btn-wa\">\r\n <span>واتساپ</span>\r\n </a>\r\n )}\r\n <a href={googleMapUrl} target=\"_blank\" className=\"btn btn-route\">\r\n <span>مسیریابی</span>\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
|
@@ -7,74 +7,94 @@
|
|
|
7
7
|
height: 100%;
|
|
8
8
|
direction: rtl;
|
|
9
9
|
font-family: inherit;
|
|
10
|
+
overflow: hidden;
|
|
10
11
|
}
|
|
11
12
|
.sidebar {
|
|
12
13
|
width: 350px;
|
|
13
|
-
background: #
|
|
14
|
-
border-left: 1px solid #
|
|
14
|
+
background: #ffffff;
|
|
15
|
+
border-left: 1px solid #e5e7eb;
|
|
15
16
|
display: flex;
|
|
16
17
|
flex-direction: column;
|
|
17
18
|
z-index: 10;
|
|
19
|
+
box-shadow: 2px 0 10px rgba(0, 0, 0, 0.05);
|
|
18
20
|
}
|
|
19
21
|
.sidebar-header {
|
|
20
22
|
padding: 20px;
|
|
21
|
-
font-weight:
|
|
22
|
-
|
|
23
|
+
font-weight: 800;
|
|
24
|
+
font-size: 18px;
|
|
25
|
+
color: #111827;
|
|
26
|
+
border-bottom: 1px solid #f3f4f6;
|
|
27
|
+
background: #fafafa;
|
|
23
28
|
}
|
|
24
29
|
.sidebar-list {
|
|
25
30
|
flex: 1;
|
|
26
31
|
overflow-y: auto;
|
|
32
|
+
scrollbar-width: thin;
|
|
27
33
|
}
|
|
28
34
|
.map-container {
|
|
29
35
|
flex: 1;
|
|
30
36
|
position: relative;
|
|
37
|
+
background: #f3f4f6;
|
|
31
38
|
}
|
|
32
39
|
.store-card {
|
|
33
|
-
padding:
|
|
34
|
-
border-bottom: 1px solid #
|
|
40
|
+
padding: 16px;
|
|
41
|
+
border-bottom: 1px solid #f3f4f6;
|
|
35
42
|
cursor: pointer;
|
|
36
|
-
transition:
|
|
43
|
+
transition: all 0.2s ease;
|
|
44
|
+
border-right: 4px solid transparent;
|
|
37
45
|
}
|
|
38
46
|
.store-card:hover {
|
|
39
|
-
background: #
|
|
47
|
+
background: #f9fafb;
|
|
40
48
|
}
|
|
41
49
|
.store-card.active {
|
|
42
|
-
background: #
|
|
43
|
-
border-right:
|
|
50
|
+
background: #eff6ff;
|
|
51
|
+
border-right-color: #2563eb;
|
|
44
52
|
}
|
|
45
53
|
.store-card-header {
|
|
46
54
|
display: flex;
|
|
47
55
|
justify-content: space-between;
|
|
56
|
+
align-items: flex-start;
|
|
48
57
|
margin-bottom: 8px;
|
|
49
58
|
}
|
|
50
59
|
.store-name {
|
|
51
60
|
margin: 0;
|
|
52
|
-
font-size:
|
|
53
|
-
|
|
61
|
+
font-size: 16px;
|
|
62
|
+
font-weight: 700;
|
|
63
|
+
color: #111827;
|
|
54
64
|
}
|
|
55
65
|
.store-city {
|
|
66
|
+
display: block;
|
|
56
67
|
font-size: 12px;
|
|
57
|
-
color: #
|
|
68
|
+
color: #6b7280;
|
|
69
|
+
margin-top: 2px;
|
|
58
70
|
}
|
|
59
71
|
.store-price {
|
|
60
72
|
color: #059669;
|
|
61
|
-
font-weight:
|
|
62
|
-
font-size:
|
|
73
|
+
font-weight: 800;
|
|
74
|
+
font-size: 15px;
|
|
75
|
+
background: #ecfdf5;
|
|
76
|
+
padding: 2px 8px;
|
|
77
|
+
border-radius: 6px;
|
|
63
78
|
}
|
|
64
79
|
.store-desc {
|
|
65
|
-
font-size:
|
|
66
|
-
color: #
|
|
80
|
+
font-size: 12.5px;
|
|
81
|
+
color: #4b5563;
|
|
67
82
|
line-height: 1.6;
|
|
83
|
+
margin: 8px 0;
|
|
84
|
+
display: -webkit-box;
|
|
85
|
+
-webkit-line-clamp: 2;
|
|
86
|
+
-webkit-box-orient: vertical;
|
|
87
|
+
overflow: hidden;
|
|
68
88
|
}
|
|
69
89
|
.store-details-wrapper {
|
|
70
90
|
display: grid;
|
|
71
91
|
grid-template-rows: 0fr;
|
|
72
|
-
transition: grid-template-rows 0.3s
|
|
92
|
+
transition: grid-template-rows 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
73
93
|
overflow: hidden;
|
|
74
94
|
}
|
|
75
95
|
.active .store-details-wrapper {
|
|
76
96
|
grid-template-rows: 1fr;
|
|
77
|
-
margin-top:
|
|
97
|
+
margin-top: 14px;
|
|
78
98
|
}
|
|
79
99
|
.store-details-content {
|
|
80
100
|
min-height: 0;
|
|
@@ -82,32 +102,89 @@
|
|
|
82
102
|
.action-buttons {
|
|
83
103
|
display: flex;
|
|
84
104
|
gap: 8px;
|
|
105
|
+
padding-bottom: 4px;
|
|
85
106
|
}
|
|
86
107
|
.btn {
|
|
87
108
|
flex: 1;
|
|
88
|
-
padding:
|
|
89
|
-
border-radius:
|
|
109
|
+
padding: 10px 5px;
|
|
110
|
+
border-radius: 8px;
|
|
90
111
|
text-align: center;
|
|
91
112
|
text-decoration: none;
|
|
92
|
-
font-size:
|
|
113
|
+
font-size: 13px;
|
|
114
|
+
font-weight: 700;
|
|
93
115
|
color: #fff;
|
|
94
|
-
transition: opacity 0.2s;
|
|
116
|
+
transition: transform 0.1s, opacity 0.2s;
|
|
95
117
|
}
|
|
96
|
-
.btn:
|
|
97
|
-
|
|
118
|
+
.btn:active {
|
|
119
|
+
transform: scale(0.95);
|
|
98
120
|
}
|
|
99
121
|
.btn-call {
|
|
100
|
-
background: #
|
|
122
|
+
background: #2563eb;
|
|
101
123
|
}
|
|
102
124
|
.btn-wa {
|
|
103
125
|
background: #10b981;
|
|
104
126
|
}
|
|
105
127
|
.btn-route {
|
|
106
|
-
background: #
|
|
128
|
+
background: #7c3aed;
|
|
129
|
+
}
|
|
130
|
+
.neshan-marker-container {
|
|
131
|
+
pointer-events: auto;
|
|
132
|
+
}
|
|
133
|
+
.neshan-marker-body {
|
|
134
|
+
display: flex;
|
|
135
|
+
flex-direction: column;
|
|
136
|
+
align-items: center;
|
|
137
|
+
transform-origin: bottom center;
|
|
138
|
+
}
|
|
139
|
+
.neshan-marker-label {
|
|
140
|
+
background: #2563eb;
|
|
141
|
+
color: #ffffff !important;
|
|
142
|
+
font-size: 12px;
|
|
143
|
+
font-weight: 900;
|
|
144
|
+
padding: 4px 12px;
|
|
145
|
+
border-radius: 20px;
|
|
146
|
+
margin-top: 6px;
|
|
147
|
+
box-shadow: 0 4px 12px rgba(37, 99, 235, 0.4);
|
|
148
|
+
border: 2px solid #ffffff;
|
|
149
|
+
white-space: nowrap;
|
|
150
|
+
pointer-events: none;
|
|
151
|
+
}
|
|
152
|
+
.neshan-cluster-marker {
|
|
153
|
+
display: flex;
|
|
154
|
+
align-items: center;
|
|
155
|
+
justify-content: center;
|
|
156
|
+
background: #ffffff;
|
|
157
|
+
border: 3px solid #2563eb;
|
|
158
|
+
border-radius: 50%;
|
|
159
|
+
width: 46px;
|
|
160
|
+
height: 46px;
|
|
161
|
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.25);
|
|
162
|
+
cursor: pointer;
|
|
163
|
+
transition: transform 0.2s ease;
|
|
164
|
+
}
|
|
165
|
+
.neshan-cluster-marker:hover {
|
|
166
|
+
transform: scale(1.1);
|
|
167
|
+
}
|
|
168
|
+
.neshan-cluster-count {
|
|
169
|
+
color: #2563eb;
|
|
170
|
+
font-weight: 900;
|
|
171
|
+
font-size: 16px;
|
|
172
|
+
}
|
|
173
|
+
.neshan-marker-selected .neshan-marker-body {
|
|
174
|
+
animation: marker-bounce 0.8s infinite alternate cubic-bezier(0.45, 0.05, 0.55, 0.95);
|
|
175
|
+
z-index: 100;
|
|
176
|
+
}
|
|
177
|
+
@keyframes marker-bounce {
|
|
178
|
+
from {
|
|
179
|
+
transform: translateY(0) scale(1.1);
|
|
180
|
+
}
|
|
181
|
+
to {
|
|
182
|
+
transform: translateY(-12px) scale(1.15);
|
|
183
|
+
}
|
|
107
184
|
}
|
|
108
185
|
.is-mobile .mobile-slider {
|
|
109
186
|
position: absolute;
|
|
110
|
-
bottom:
|
|
187
|
+
bottom: 24px;
|
|
111
188
|
left: 0;
|
|
112
189
|
right: 0;
|
|
113
190
|
display: flex;
|
|
@@ -116,25 +193,34 @@
|
|
|
116
193
|
overflow-x: auto;
|
|
117
194
|
scroll-snap-type: x mandatory;
|
|
118
195
|
pointer-events: none;
|
|
196
|
+
z-index: 20;
|
|
197
|
+
}
|
|
198
|
+
.is-mobile .mobile-slider::-webkit-scrollbar {
|
|
199
|
+
display: none;
|
|
119
200
|
}
|
|
120
201
|
.is-mobile .store-card {
|
|
121
|
-
min-width:
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
202
|
+
min-width: 300px;
|
|
203
|
+
max-width: 85vw;
|
|
204
|
+
background: #ffffff;
|
|
205
|
+
border-radius: 16px;
|
|
206
|
+
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
|
|
125
207
|
pointer-events: auto;
|
|
126
208
|
scroll-snap-align: center;
|
|
209
|
+
border: none;
|
|
127
210
|
}
|
|
128
211
|
.product-badge {
|
|
129
212
|
position: absolute;
|
|
130
213
|
top: 20px;
|
|
131
214
|
right: 20px;
|
|
132
|
-
background: rgba(255, 255, 255, 0.
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
font-
|
|
215
|
+
background: rgba(255, 255, 255, 0.95);
|
|
216
|
+
backdrop-filter: blur(4px);
|
|
217
|
+
padding: 10px 18px;
|
|
218
|
+
border-radius: 12px;
|
|
219
|
+
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
|
|
220
|
+
font-weight: 800;
|
|
221
|
+
font-size: 14px;
|
|
222
|
+
color: #1f2937;
|
|
138
223
|
z-index: 5;
|
|
224
|
+
border: 1px solid rgba(0, 0, 0, 0.05);
|
|
139
225
|
}
|
|
140
226
|
/*# sourceMappingURL=index.css.map */
|
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.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}\r\n\r\n
|
|
1
|
+
{"version":3,"sources":["../src/styles.css"],"sourcesContent":["\r\n@import \"@neshan-maps-platform/mapbox-gl/dist/NeshanMapboxGl.css\";\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: 10;\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 background: #fafafa;\r\n}\r\n\r\n.sidebar-list {\r\n flex: 1;\r\n overflow-y: auto;\r\n scrollbar-width: thin;\r\n}\r\n\r\n\r\n.map-container {\r\n flex: 1;\r\n position: relative;\r\n background: #f3f4f6;\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: all 0.2s ease;\r\n border-right: 4px solid transparent;\r\n}\r\n\r\n.store-card:hover {\r\n background: #f9fafb;\r\n}\r\n\r\n.store-card.active {\r\n background: #eff6ff;\r\n border-right-color: #2563eb;\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: 8px;\r\n}\r\n\r\n.store-name {\r\n margin: 0;\r\n font-size: 16px;\r\n font-weight: 700;\r\n color: #111827;\r\n}\r\n\r\n.store-city {\r\n display: block;\r\n font-size: 12px;\r\n color: #6b7280;\r\n margin-top: 2px;\r\n}\r\n\r\n.store-price {\r\n color: #059669;\r\n font-weight: 800;\r\n font-size: 15px;\r\n background: #ecfdf5;\r\n padding: 2px 8px;\r\n border-radius: 6px;\r\n}\r\n\r\n.store-desc {\r\n font-size: 12.5px;\r\n color: #4b5563;\r\n line-height: 1.6;\r\n margin: 8px 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-details-wrapper {\r\n display: grid;\r\n grid-template-rows: 0fr;\r\n transition: grid-template-rows 0.3s cubic-bezier(0.4, 0, 0.2, 1);\r\n overflow: hidden;\r\n}\r\n\r\n.active .store-details-wrapper {\r\n grid-template-rows: 1fr;\r\n margin-top: 14px;\r\n}\r\n\r\n.store-details-content {\r\n min-height: 0;\r\n}\r\n\r\n.action-buttons {\r\n display: flex;\r\n gap: 8px;\r\n padding-bottom: 4px;\r\n}\r\n\r\n.btn {\r\n flex: 1;\r\n padding: 10px 5px;\r\n border-radius: 8px;\r\n text-align: center;\r\n text-decoration: none;\r\n font-size: 13px;\r\n font-weight: 700;\r\n color: #fff;\r\n transition: transform 0.1s, opacity 0.2s;\r\n}\r\n\r\n.btn:active { transform: scale(0.95); }\r\n.btn-call { background: #2563eb; }\r\n.btn-wa { background: #10b981; }\r\n.btn-route { background: #7c3aed; }\r\n\r\n\r\n.neshan-marker-container {\r\n pointer-events: auto;\r\n}\r\n\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: 46px;\r\n height: 46px;\r\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.25);\r\n cursor: pointer;\r\n transition: transform 0.2s ease;\r\n}\r\n\r\n.neshan-cluster-marker:hover {\r\n transform: scale(1.1);\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;\r\n bottom: 24px;\r\n left: 0;\r\n right: 0;\r\n display: flex;\r\n gap: 12px;\r\n padding: 0 20px;\r\n overflow-x: auto;\r\n scroll-snap-type: x mandatory;\r\n pointer-events: none;\r\n z-index: 20;\r\n}\r\n\r\n.is-mobile .mobile-slider::-webkit-scrollbar { display: none; }\r\n\r\n.is-mobile .store-card {\r\n min-width: 300px;\r\n max-width: 85vw;\r\n background: #ffffff;\r\n border-radius: 16px;\r\n box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);\r\n pointer-events: auto;\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 top: 20px;\r\n right: 20px;\r\n background: rgba(255, 255, 255, 0.95);\r\n backdrop-filter: blur(4px);\r\n padding: 10px 18px;\r\n border-radius: 12px;\r\n box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);\r\n font-weight: 800;\r\n font-size: 14px;\r\n color: #1f2937;\r\n z-index: 5;\r\n border: 1px solid rgba(0, 0, 0, 0.05);\r\n}"],"mappings":"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","names":[]}
|