mehdi-akbari-map 0.1.0 → 0.1.1

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 CHANGED
@@ -30,10 +30,14 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var src_exports = {};
32
32
  __export(src_exports, {
33
- NeshanMap: () => Map_default
33
+ NeshanMap: () => InteractiveMap,
34
+ RawMap: () => Map_default
34
35
  });
35
36
  module.exports = __toCommonJS(src_exports);
36
37
 
38
+ // src/components/InteractiveMap.tsx
39
+ var import_react2 = require("react");
40
+
37
41
  // src/components/Map.tsx
38
42
  var import_react = require("react");
39
43
 
@@ -180,8 +184,93 @@ var Map = ({
180
184
  );
181
185
  };
182
186
  var Map_default = Map;
187
+
188
+ // src/components/StoreCard.tsx
189
+ var import_jsx_runtime2 = require("react/jsx-runtime");
190
+ var StoreCard = ({ store, isSelected, onSelect }) => {
191
+ const googleMapUrl = `https://www.google.com/maps/dir/?api=1&destination=${store.lat},${store.lng}`;
192
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
193
+ "div",
194
+ {
195
+ className: `store-card ${isSelected ? "active" : ""}`,
196
+ onClick: onSelect,
197
+ children: [
198
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "store-card-header", children: [
199
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
200
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h4", { className: "store-name", children: store.name }),
201
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "store-city", children: store.city })
202
+ ] }),
203
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "store-price", children: [
204
+ store.price,
205
+ " \u062A\u0648\u0645\u0627\u0646"
206
+ ] })
207
+ ] }),
208
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "store-desc", children: store.desc }),
209
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "store-details-wrapper", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "store-details-content", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "action-buttons", children: [
210
+ store.phone && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("a", { href: `tel:${store.phone}`, className: "btn btn-call", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: "\u062A\u0645\u0627\u0633" }) }),
211
+ store.whatsapp && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("a", { href: `https://wa.me/${store.whatsapp}`, target: "_blank", className: "btn btn-wa", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: "\u0648\u0627\u062A\u0633\u0627\u067E" }) }),
212
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("a", { href: googleMapUrl, target: "_blank", className: "btn btn-route", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: "\u0645\u0633\u06CC\u0631\u06CC\u0627\u0628\u06CC" }) })
213
+ ] }) }) })
214
+ ]
215
+ }
216
+ );
217
+ };
218
+
219
+ // src/components/InteractiveMap.tsx
220
+ var import_jsx_runtime3 = require("react/jsx-runtime");
221
+ var InteractiveMap = (props) => {
222
+ const [isMobile, setIsMobile] = (0, import_react2.useState)(false);
223
+ const [mapInstance, setMapInstance] = (0, import_react2.useState)(null);
224
+ (0, import_react2.useEffect)(() => {
225
+ const checkMobile = () => setIsMobile(window.innerWidth < 1024);
226
+ checkMobile();
227
+ window.addEventListener("resize", checkMobile);
228
+ return () => window.removeEventListener("resize", checkMobile);
229
+ }, []);
230
+ const handleStoreSelect = (store, index) => {
231
+ props.onMarkerClick?.(store, index, mapInstance);
232
+ };
233
+ const safeMarkers = props.markers || [];
234
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: `map-layout-root ${isMobile ? "is-mobile" : "is-desktop"}`, children: [
235
+ !isMobile && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("aside", { className: "sidebar", children: [
236
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("header", { className: "sidebar-header", children: "\u0644\u06CC\u0633\u062A \u0641\u0631\u0648\u0634\u06AF\u0627\u0647\u200C\u0647\u0627" }),
237
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "sidebar-list", children: safeMarkers.map((store, idx) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
238
+ StoreCard,
239
+ {
240
+ store,
241
+ isSelected: props.selectedMarkerId === store.id,
242
+ onSelect: () => handleStoreSelect(store, idx)
243
+ },
244
+ store.id
245
+ )) })
246
+ ] }),
247
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("main", { className: "map-container", children: [
248
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
249
+ Map_default,
250
+ {
251
+ ...props,
252
+ onMapLoad: (map) => {
253
+ setMapInstance(map);
254
+ props.onMapLoad?.(map);
255
+ }
256
+ }
257
+ ),
258
+ props.productName && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "product-badge", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: props.productName }) }),
259
+ isMobile && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mobile-slider", children: safeMarkers.map((store, idx) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
260
+ StoreCard,
261
+ {
262
+ store,
263
+ isSelected: props.selectedMarkerId === store.id,
264
+ onSelect: () => handleStoreSelect(store, idx)
265
+ },
266
+ store.id
267
+ )) })
268
+ ] })
269
+ ] });
270
+ };
183
271
  // Annotate the CommonJS export names for ESM import in node:
184
272
  0 && (module.exports = {
185
- NeshanMap
273
+ NeshanMap,
274
+ RawMap
186
275
  });
187
276
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/components/Map.tsx","../src/utils/createCustomMarkerElement.ts"],"sourcesContent":["\r\n\r\n\r\nexport { default as NeshanMap } from './components/Map';\r\n\r\n\r\nexport type {\r\n MapProps,\r\n MapOptions,\r\n MarkerData,\r\n MapboxMap,\r\n LngLatLike,\r\n} from './types';\r\n\r\n\r\n\r\n\r\n\r\n\r\nimport './styles.css';","\"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\nimport '@neshan-maps-platform/mapbox-gl/dist/NeshanMapboxGl.css';\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}"],"mappings":"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","names":[]}
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\nimport '@neshan-maps-platform/mapbox-gl/dist/NeshanMapboxGl.css';\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"]}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- export { default as NeshanMap } from './components/Map';
2
- export type { MapProps, MapOptions, MarkerData, MapboxMap, LngLatLike, } from './types';
3
1
  import './styles.css';
2
+ export { InteractiveMap as NeshanMap } from './components/InteractiveMap';
3
+ export { default as RawMap } from './components/Map';
4
+ export type { MapProps, MapOptions, MarkerData, Store, MapboxMap, LngLatLike, } from './types';
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAGxD,YAAY,EACV,QAAQ,EACR,UAAU,EACV,UAAU,EACV,SAAS,EACT,UAAU,GACX,MAAM,SAAS,CAAC;AAOjB,OAAO,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,cAAc,CAAC;AAGtB,OAAO,EAAE,cAAc,IAAI,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAG1E,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAGrD,YAAY,EACV,QAAQ,EACR,UAAU,EACV,UAAU,EACV,KAAK,EACL,SAAS,EACT,UAAU,GACX,MAAM,SAAS,CAAC"}
package/dist/index.js CHANGED
@@ -1,3 +1,6 @@
1
+ // src/components/InteractiveMap.tsx
2
+ import { useState as useState2, useEffect as useEffect2 } from "react";
3
+
1
4
  // src/components/Map.tsx
2
5
  import { useEffect, useRef, useState } from "react";
3
6
 
@@ -144,7 +147,92 @@ var Map = ({
144
147
  );
145
148
  };
146
149
  var Map_default = Map;
150
+
151
+ // src/components/StoreCard.tsx
152
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
153
+ var StoreCard = ({ store, isSelected, onSelect }) => {
154
+ const googleMapUrl = `https://www.google.com/maps/dir/?api=1&destination=${store.lat},${store.lng}`;
155
+ return /* @__PURE__ */ jsxs(
156
+ "div",
157
+ {
158
+ className: `store-card ${isSelected ? "active" : ""}`,
159
+ onClick: onSelect,
160
+ children: [
161
+ /* @__PURE__ */ jsxs("div", { className: "store-card-header", children: [
162
+ /* @__PURE__ */ jsxs("div", { children: [
163
+ /* @__PURE__ */ jsx2("h4", { className: "store-name", children: store.name }),
164
+ /* @__PURE__ */ jsx2("span", { className: "store-city", children: store.city })
165
+ ] }),
166
+ /* @__PURE__ */ jsxs("div", { className: "store-price", children: [
167
+ store.price,
168
+ " \u062A\u0648\u0645\u0627\u0646"
169
+ ] })
170
+ ] }),
171
+ /* @__PURE__ */ jsx2("p", { className: "store-desc", children: store.desc }),
172
+ /* @__PURE__ */ jsx2("div", { className: "store-details-wrapper", children: /* @__PURE__ */ jsx2("div", { className: "store-details-content", children: /* @__PURE__ */ jsxs("div", { className: "action-buttons", children: [
173
+ store.phone && /* @__PURE__ */ jsx2("a", { href: `tel:${store.phone}`, className: "btn btn-call", children: /* @__PURE__ */ jsx2("span", { children: "\u062A\u0645\u0627\u0633" }) }),
174
+ store.whatsapp && /* @__PURE__ */ jsx2("a", { href: `https://wa.me/${store.whatsapp}`, target: "_blank", className: "btn btn-wa", children: /* @__PURE__ */ jsx2("span", { children: "\u0648\u0627\u062A\u0633\u0627\u067E" }) }),
175
+ /* @__PURE__ */ jsx2("a", { href: googleMapUrl, target: "_blank", className: "btn btn-route", children: /* @__PURE__ */ jsx2("span", { children: "\u0645\u0633\u06CC\u0631\u06CC\u0627\u0628\u06CC" }) })
176
+ ] }) }) })
177
+ ]
178
+ }
179
+ );
180
+ };
181
+
182
+ // src/components/InteractiveMap.tsx
183
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
184
+ var InteractiveMap = (props) => {
185
+ const [isMobile, setIsMobile] = useState2(false);
186
+ const [mapInstance, setMapInstance] = useState2(null);
187
+ useEffect2(() => {
188
+ const checkMobile = () => setIsMobile(window.innerWidth < 1024);
189
+ checkMobile();
190
+ window.addEventListener("resize", checkMobile);
191
+ return () => window.removeEventListener("resize", checkMobile);
192
+ }, []);
193
+ const handleStoreSelect = (store, index) => {
194
+ props.onMarkerClick?.(store, index, mapInstance);
195
+ };
196
+ const safeMarkers = props.markers || [];
197
+ return /* @__PURE__ */ jsxs2("div", { className: `map-layout-root ${isMobile ? "is-mobile" : "is-desktop"}`, children: [
198
+ !isMobile && /* @__PURE__ */ jsxs2("aside", { className: "sidebar", children: [
199
+ /* @__PURE__ */ jsx3("header", { className: "sidebar-header", children: "\u0644\u06CC\u0633\u062A \u0641\u0631\u0648\u0634\u06AF\u0627\u0647\u200C\u0647\u0627" }),
200
+ /* @__PURE__ */ jsx3("div", { className: "sidebar-list", children: safeMarkers.map((store, idx) => /* @__PURE__ */ jsx3(
201
+ StoreCard,
202
+ {
203
+ store,
204
+ isSelected: props.selectedMarkerId === store.id,
205
+ onSelect: () => handleStoreSelect(store, idx)
206
+ },
207
+ store.id
208
+ )) })
209
+ ] }),
210
+ /* @__PURE__ */ jsxs2("main", { className: "map-container", children: [
211
+ /* @__PURE__ */ jsx3(
212
+ Map_default,
213
+ {
214
+ ...props,
215
+ onMapLoad: (map) => {
216
+ setMapInstance(map);
217
+ props.onMapLoad?.(map);
218
+ }
219
+ }
220
+ ),
221
+ props.productName && /* @__PURE__ */ jsx3("div", { className: "product-badge", children: /* @__PURE__ */ jsx3("span", { children: props.productName }) }),
222
+ isMobile && /* @__PURE__ */ jsx3("div", { className: "mobile-slider", children: safeMarkers.map((store, idx) => /* @__PURE__ */ jsx3(
223
+ StoreCard,
224
+ {
225
+ store,
226
+ isSelected: props.selectedMarkerId === store.id,
227
+ onSelect: () => handleStoreSelect(store, idx)
228
+ },
229
+ store.id
230
+ )) })
231
+ ] })
232
+ ] });
233
+ };
147
234
  export {
148
- Map_default as NeshanMap
235
+ InteractiveMap as NeshanMap,
236
+ Map_default as RawMap
149
237
  };
150
238
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/Map.tsx","../src/utils/createCustomMarkerElement.ts"],"sourcesContent":["\"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\nimport '@neshan-maps-platform/mapbox-gl/dist/NeshanMapboxGl.css';\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}"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/components/InteractiveMap.tsx","../src/components/Map.tsx","../src/utils/createCustomMarkerElement.ts","../src/components/StoreCard.tsx"],"sourcesContent":["\"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\nimport '@neshan-maps-platform/mapbox-gl/dist/NeshanMapboxGl.css';\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":["useState","useEffect","jsx","jsx","jsxs","useState","useEffect"]}
package/dist/react.cjs CHANGED
@@ -31,10 +31,13 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  // src/react.ts
32
32
  var react_exports = {};
33
33
  __export(react_exports, {
34
- NeshanMap: () => Map_default
34
+ NeshanMap: () => InteractiveMap
35
35
  });
36
36
  module.exports = __toCommonJS(react_exports);
37
37
 
38
+ // src/components/InteractiveMap.tsx
39
+ var import_react2 = require("react");
40
+
38
41
  // src/components/Map.tsx
39
42
  var import_react = require("react");
40
43
 
@@ -181,6 +184,90 @@ var Map = ({
181
184
  );
182
185
  };
183
186
  var Map_default = Map;
187
+
188
+ // src/components/StoreCard.tsx
189
+ var import_jsx_runtime2 = require("react/jsx-runtime");
190
+ var StoreCard = ({ store, isSelected, onSelect }) => {
191
+ const googleMapUrl = `https://www.google.com/maps/dir/?api=1&destination=${store.lat},${store.lng}`;
192
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
193
+ "div",
194
+ {
195
+ className: `store-card ${isSelected ? "active" : ""}`,
196
+ onClick: onSelect,
197
+ children: [
198
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "store-card-header", children: [
199
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
200
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h4", { className: "store-name", children: store.name }),
201
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "store-city", children: store.city })
202
+ ] }),
203
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "store-price", children: [
204
+ store.price,
205
+ " \u062A\u0648\u0645\u0627\u0646"
206
+ ] })
207
+ ] }),
208
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "store-desc", children: store.desc }),
209
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "store-details-wrapper", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "store-details-content", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "action-buttons", children: [
210
+ store.phone && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("a", { href: `tel:${store.phone}`, className: "btn btn-call", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: "\u062A\u0645\u0627\u0633" }) }),
211
+ store.whatsapp && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("a", { href: `https://wa.me/${store.whatsapp}`, target: "_blank", className: "btn btn-wa", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: "\u0648\u0627\u062A\u0633\u0627\u067E" }) }),
212
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("a", { href: googleMapUrl, target: "_blank", className: "btn btn-route", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: "\u0645\u0633\u06CC\u0631\u06CC\u0627\u0628\u06CC" }) })
213
+ ] }) }) })
214
+ ]
215
+ }
216
+ );
217
+ };
218
+
219
+ // src/components/InteractiveMap.tsx
220
+ var import_jsx_runtime3 = require("react/jsx-runtime");
221
+ var InteractiveMap = (props) => {
222
+ const [isMobile, setIsMobile] = (0, import_react2.useState)(false);
223
+ const [mapInstance, setMapInstance] = (0, import_react2.useState)(null);
224
+ (0, import_react2.useEffect)(() => {
225
+ const checkMobile = () => setIsMobile(window.innerWidth < 1024);
226
+ checkMobile();
227
+ window.addEventListener("resize", checkMobile);
228
+ return () => window.removeEventListener("resize", checkMobile);
229
+ }, []);
230
+ const handleStoreSelect = (store, index) => {
231
+ props.onMarkerClick?.(store, index, mapInstance);
232
+ };
233
+ const safeMarkers = props.markers || [];
234
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: `map-layout-root ${isMobile ? "is-mobile" : "is-desktop"}`, children: [
235
+ !isMobile && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("aside", { className: "sidebar", children: [
236
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("header", { className: "sidebar-header", children: "\u0644\u06CC\u0633\u062A \u0641\u0631\u0648\u0634\u06AF\u0627\u0647\u200C\u0647\u0627" }),
237
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "sidebar-list", children: safeMarkers.map((store, idx) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
238
+ StoreCard,
239
+ {
240
+ store,
241
+ isSelected: props.selectedMarkerId === store.id,
242
+ onSelect: () => handleStoreSelect(store, idx)
243
+ },
244
+ store.id
245
+ )) })
246
+ ] }),
247
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("main", { className: "map-container", children: [
248
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
249
+ Map_default,
250
+ {
251
+ ...props,
252
+ onMapLoad: (map) => {
253
+ setMapInstance(map);
254
+ props.onMapLoad?.(map);
255
+ }
256
+ }
257
+ ),
258
+ props.productName && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "product-badge", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: props.productName }) }),
259
+ isMobile && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mobile-slider", children: safeMarkers.map((store, idx) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
260
+ StoreCard,
261
+ {
262
+ store,
263
+ isSelected: props.selectedMarkerId === store.id,
264
+ onSelect: () => handleStoreSelect(store, idx)
265
+ },
266
+ store.id
267
+ )) })
268
+ ] })
269
+ ] });
270
+ };
184
271
  // Annotate the CommonJS export names for ESM import in node:
185
272
  0 && (module.exports = {
186
273
  NeshanMap
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/react.ts","../src/components/Map.tsx","../src/utils/createCustomMarkerElement.ts"],"sourcesContent":["// src/react.ts\r\n\r\n\"use client\";\r\n\r\nexport { default as NeshanMap } from './components/Map';\r\nexport type {\r\n MapProps,\r\n MapOptions,\r\n MarkerData,\r\n} from './types';","\"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\nimport '@neshan-maps-platform/mapbox-gl/dist/NeshanMapboxGl.css';\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}"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/react.ts","../src/components/InteractiveMap.tsx","../src/components/Map.tsx","../src/utils/createCustomMarkerElement.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 } 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\nimport '@neshan-maps-platform/mapbox-gl/dist/NeshanMapboxGl.css';\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"]}
package/dist/react.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- export { default as NeshanMap } from './components/Map';
2
- export type { MapProps, MapOptions, MarkerData, } from './types';
1
+ export { InteractiveMap as NeshanMap } from './components/InteractiveMap';
2
+ export type { MapProps, MapOptions, MarkerData, Store, } from './types';
3
3
  //# sourceMappingURL=react.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACxD,YAAY,EACV,QAAQ,EACR,UAAU,EACV,UAAU,GACX,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,IAAI,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAG1E,YAAY,EACV,QAAQ,EACR,UAAU,EACV,UAAU,EACV,KAAK,GACN,MAAM,SAAS,CAAC"}
package/dist/react.js CHANGED
@@ -1,5 +1,8 @@
1
1
  "use client";
2
2
 
3
+ // src/components/InteractiveMap.tsx
4
+ import { useState as useState2, useEffect as useEffect2 } from "react";
5
+
3
6
  // src/components/Map.tsx
4
7
  import { useEffect, useRef, useState } from "react";
5
8
 
@@ -146,7 +149,91 @@ var Map = ({
146
149
  );
147
150
  };
148
151
  var Map_default = Map;
152
+
153
+ // src/components/StoreCard.tsx
154
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
155
+ var StoreCard = ({ store, isSelected, onSelect }) => {
156
+ const googleMapUrl = `https://www.google.com/maps/dir/?api=1&destination=${store.lat},${store.lng}`;
157
+ return /* @__PURE__ */ jsxs(
158
+ "div",
159
+ {
160
+ className: `store-card ${isSelected ? "active" : ""}`,
161
+ onClick: onSelect,
162
+ children: [
163
+ /* @__PURE__ */ jsxs("div", { className: "store-card-header", children: [
164
+ /* @__PURE__ */ jsxs("div", { children: [
165
+ /* @__PURE__ */ jsx2("h4", { className: "store-name", children: store.name }),
166
+ /* @__PURE__ */ jsx2("span", { className: "store-city", children: store.city })
167
+ ] }),
168
+ /* @__PURE__ */ jsxs("div", { className: "store-price", children: [
169
+ store.price,
170
+ " \u062A\u0648\u0645\u0627\u0646"
171
+ ] })
172
+ ] }),
173
+ /* @__PURE__ */ jsx2("p", { className: "store-desc", children: store.desc }),
174
+ /* @__PURE__ */ jsx2("div", { className: "store-details-wrapper", children: /* @__PURE__ */ jsx2("div", { className: "store-details-content", children: /* @__PURE__ */ jsxs("div", { className: "action-buttons", children: [
175
+ store.phone && /* @__PURE__ */ jsx2("a", { href: `tel:${store.phone}`, className: "btn btn-call", children: /* @__PURE__ */ jsx2("span", { children: "\u062A\u0645\u0627\u0633" }) }),
176
+ store.whatsapp && /* @__PURE__ */ jsx2("a", { href: `https://wa.me/${store.whatsapp}`, target: "_blank", className: "btn btn-wa", children: /* @__PURE__ */ jsx2("span", { children: "\u0648\u0627\u062A\u0633\u0627\u067E" }) }),
177
+ /* @__PURE__ */ jsx2("a", { href: googleMapUrl, target: "_blank", className: "btn btn-route", children: /* @__PURE__ */ jsx2("span", { children: "\u0645\u0633\u06CC\u0631\u06CC\u0627\u0628\u06CC" }) })
178
+ ] }) }) })
179
+ ]
180
+ }
181
+ );
182
+ };
183
+
184
+ // src/components/InteractiveMap.tsx
185
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
186
+ var InteractiveMap = (props) => {
187
+ const [isMobile, setIsMobile] = useState2(false);
188
+ const [mapInstance, setMapInstance] = useState2(null);
189
+ useEffect2(() => {
190
+ const checkMobile = () => setIsMobile(window.innerWidth < 1024);
191
+ checkMobile();
192
+ window.addEventListener("resize", checkMobile);
193
+ return () => window.removeEventListener("resize", checkMobile);
194
+ }, []);
195
+ const handleStoreSelect = (store, index) => {
196
+ props.onMarkerClick?.(store, index, mapInstance);
197
+ };
198
+ const safeMarkers = props.markers || [];
199
+ return /* @__PURE__ */ jsxs2("div", { className: `map-layout-root ${isMobile ? "is-mobile" : "is-desktop"}`, children: [
200
+ !isMobile && /* @__PURE__ */ jsxs2("aside", { className: "sidebar", children: [
201
+ /* @__PURE__ */ jsx3("header", { className: "sidebar-header", children: "\u0644\u06CC\u0633\u062A \u0641\u0631\u0648\u0634\u06AF\u0627\u0647\u200C\u0647\u0627" }),
202
+ /* @__PURE__ */ jsx3("div", { className: "sidebar-list", children: safeMarkers.map((store, idx) => /* @__PURE__ */ jsx3(
203
+ StoreCard,
204
+ {
205
+ store,
206
+ isSelected: props.selectedMarkerId === store.id,
207
+ onSelect: () => handleStoreSelect(store, idx)
208
+ },
209
+ store.id
210
+ )) })
211
+ ] }),
212
+ /* @__PURE__ */ jsxs2("main", { className: "map-container", children: [
213
+ /* @__PURE__ */ jsx3(
214
+ Map_default,
215
+ {
216
+ ...props,
217
+ onMapLoad: (map) => {
218
+ setMapInstance(map);
219
+ props.onMapLoad?.(map);
220
+ }
221
+ }
222
+ ),
223
+ props.productName && /* @__PURE__ */ jsx3("div", { className: "product-badge", children: /* @__PURE__ */ jsx3("span", { children: props.productName }) }),
224
+ isMobile && /* @__PURE__ */ jsx3("div", { className: "mobile-slider", children: safeMarkers.map((store, idx) => /* @__PURE__ */ jsx3(
225
+ StoreCard,
226
+ {
227
+ store,
228
+ isSelected: props.selectedMarkerId === store.id,
229
+ onSelect: () => handleStoreSelect(store, idx)
230
+ },
231
+ store.id
232
+ )) })
233
+ ] })
234
+ ] });
235
+ };
149
236
  export {
150
- Map_default as NeshanMap
237
+ InteractiveMap as NeshanMap
151
238
  };
152
239
  //# sourceMappingURL=react.js.map
package/dist/react.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/Map.tsx","../src/utils/createCustomMarkerElement.ts"],"sourcesContent":["\"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\nimport '@neshan-maps-platform/mapbox-gl/dist/NeshanMapboxGl.css';\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}"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/components/InteractiveMap.tsx","../src/components/Map.tsx","../src/utils/createCustomMarkerElement.ts","../src/components/StoreCard.tsx"],"sourcesContent":["\"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\nimport '@neshan-maps-platform/mapbox-gl/dist/NeshanMapboxGl.css';\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":["useState","useEffect","jsx","jsx","jsxs","useState","useEffect"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mehdi-akbari-map",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "A professional Map",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",