mehdi-akbari-map 0.0.6 → 0.0.8

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.
@@ -1 +1 @@
1
- {"version":3,"file":"Map.d.ts","sourceRoot":"","sources":["../../src/components/Map.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAsC,MAAM,OAAO,CAAC;AAC3D,OAAO,KAAK,EAAyB,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEhE,OAAO,yDAAyD,CAAC;AAEjE,QAAA,MAAM,GAAG,EAAE,KAAK,CAAC,EAAE,CAAC,QAAQ,CA2E3B,CAAC;AAEF,eAAe,GAAG,CAAC"}
1
+ {"version":3,"file":"Map.d.ts","sourceRoot":"","sources":["../../src/components/Map.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAsC,MAAM,OAAO,CAAC;AAC3D,OAAO,KAAK,EAAyB,QAAQ,EAAE,MAAM,UAAU,CAAC;AAIhE,OAAO,yDAAyD,CAAC;AAEjE,QAAA,MAAM,GAAG,EAAE,KAAK,CAAC,EAAE,CAAC,QAAQ,CAgH3B,CAAC;AAEF,eAAe,GAAG,CAAC"}
package/dist/index.cjs CHANGED
@@ -44,24 +44,25 @@ function createCustomMarkerElement({
44
44
  logoSrc = "",
45
45
  showPrice = true
46
46
  }) {
47
- const wrapper = document.createElement("div");
48
- Object.assign(wrapper.style, {
47
+ const container = document.createElement("div");
48
+ container.className = "neshan-marker-container";
49
+ const body = document.createElement("div");
50
+ body.className = `neshan-marker-body ${isSelected ? "neshan-marker-selected" : ""}`;
51
+ Object.assign(body.style, {
49
52
  display: "flex",
50
53
  flexDirection: "column",
51
54
  alignItems: "center",
52
- cursor: "pointer",
53
- transition: "all 0.3s ease",
54
- transform: isSelected ? "scale(1.2)" : "scale(1)",
55
- zIndex: isSelected ? "10" : "1"
55
+ transition: "transform 0.3s ease",
56
+ transform: isSelected ? "scale(1.2)" : "scale(1)"
56
57
  });
57
- const iconTarget = document.createElement("div");
58
- Object.assign(iconTarget.style, {
58
+ const iconBox = document.createElement("div");
59
+ Object.assign(iconBox.style, {
59
60
  width: "40px",
60
61
  height: "40px",
61
62
  borderRadius: "50%",
62
63
  backgroundColor: "white",
63
64
  border: isSelected ? "3px solid #3b82f6" : "2px solid #ef4444",
64
- boxShadow: "0 4px 6px rgba(0,0,0,0.1)",
65
+ boxShadow: "0 4px 8px rgba(0,0,0,0.2)",
65
66
  display: "flex",
66
67
  alignItems: "center",
67
68
  justifyContent: "center",
@@ -70,31 +71,22 @@ function createCustomMarkerElement({
70
71
  if (logoSrc) {
71
72
  const img = document.createElement("img");
72
73
  img.src = logoSrc;
73
- img.style.width = "80%";
74
- img.style.height = "80%";
75
- img.style.objectFit = "contain";
76
- iconTarget.appendChild(img);
74
+ img.style.width = "100%";
75
+ img.style.height = "100%";
76
+ img.style.objectFit = "cover";
77
+ iconBox.appendChild(img);
77
78
  } else {
78
- iconTarget.style.backgroundColor = "#ef4444";
79
+ iconBox.style.backgroundColor = "#ef4444";
79
80
  }
80
- wrapper.appendChild(iconTarget);
81
- if (showPrice && (marker.name || marker.price)) {
81
+ body.appendChild(iconBox);
82
+ if (showPrice && (marker.price || marker.name)) {
82
83
  const label = document.createElement("div");
83
- Object.assign(label.style, {
84
- backgroundColor: "white",
85
- padding: "2px 8px",
86
- borderRadius: "4px",
87
- marginTop: "4px",
88
- fontSize: "11px",
89
- fontWeight: "bold",
90
- whiteSpace: "nowrap",
91
- boxShadow: "0 2px 4px rgba(0,0,0,0.2)",
92
- border: "1px solid #ddd"
93
- });
94
- const labelText = marker.price ? `${marker.price} \u062A` : marker.name;
95
- label.textContent = labelText || "";
84
+ label.className = "neshan-marker-label";
85
+ label.textContent = marker.price ? `${marker.price} \u062A` : marker.name ?? "";
86
+ body.appendChild(label);
96
87
  }
97
- return wrapper;
88
+ container.appendChild(body);
89
+ return container;
98
90
  }
99
91
 
100
92
  // src/components/Map.tsx
@@ -103,19 +95,27 @@ var import_jsx_runtime = require("react/jsx-runtime");
103
95
  var Map = ({
104
96
  options,
105
97
  markers = [],
98
+ markerLogoUrl = "",
106
99
  selectedMarkerId = null,
107
100
  onMarkerClick,
101
+ onMapLoad,
108
102
  className = "",
109
103
  style = { width: "100%", height: "100%" }
110
104
  }) => {
111
105
  const mapContainerRef = (0, import_react.useRef)(null);
112
106
  const [mapInstance, setMapInstance] = (0, import_react.useState)(null);
113
- const markersRef = (0, import_react.useRef)([]);
114
107
  const [mapLib, setMapLib] = (0, import_react.useState)(null);
108
+ const markersRef = (0, import_react.useRef)([]);
115
109
  (0, import_react.useEffect)(() => {
110
+ let isMounted = true;
116
111
  import("@neshan-maps-platform/mapbox-gl").then((mod) => {
117
- setMapLib(mod.default || mod);
112
+ if (isMounted) {
113
+ setMapLib(mod.default || mod);
114
+ }
118
115
  });
116
+ return () => {
117
+ isMounted = false;
118
+ };
119
119
  }, []);
120
120
  (0, import_react.useEffect)(() => {
121
121
  if (!mapLib || !mapContainerRef.current || mapInstance) return;
@@ -125,8 +125,13 @@ var Map = ({
125
125
  });
126
126
  map.on("load", () => {
127
127
  setMapInstance(map);
128
+ onMapLoad?.(map);
128
129
  });
129
130
  return () => {
131
+ if (map) {
132
+ map.remove();
133
+ setMapInstance(null);
134
+ }
130
135
  };
131
136
  }, [mapLib]);
132
137
  (0, import_react.useEffect)(() => {
@@ -137,11 +142,13 @@ var Map = ({
137
142
  const isSelected = markerData.id === selectedMarkerId;
138
143
  const el = createCustomMarkerElement({
139
144
  marker: markerData,
140
- isSelected
145
+ isSelected,
146
+ logoSrc: markerLogoUrl
141
147
  });
142
148
  const marker = new mapLib.Marker({
143
149
  element: el,
144
- anchor: "bottom"
150
+ anchor: "bottom",
151
+ offset: [0, 0]
145
152
  }).setLngLat([markerData.lng, markerData.lat]).addTo(mapInstance);
146
153
  el.addEventListener("click", (e) => {
147
154
  e.stopPropagation();
@@ -149,8 +156,28 @@ var Map = ({
149
156
  });
150
157
  markersRef.current.push(marker);
151
158
  });
152
- }, [markers, mapInstance, selectedMarkerId]);
153
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { position: "relative", ...style }, className, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref: mapContainerRef, style: { width: "100%", height: "100%" } }) });
159
+ return () => {
160
+ markersRef.current.forEach((m) => m.remove());
161
+ };
162
+ }, [markers, mapInstance, mapLib, selectedMarkerId, markerLogoUrl, onMarkerClick]);
163
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
164
+ "div",
165
+ {
166
+ style: {
167
+ position: "relative",
168
+ overflow: "hidden",
169
+ ...style
170
+ },
171
+ className: `neshan-map-wrapper ${className}`,
172
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
173
+ "div",
174
+ {
175
+ ref: mapContainerRef,
176
+ style: { width: "100%", height: "100%" }
177
+ }
178
+ )
179
+ }
180
+ );
154
181
  };
155
182
  var Map_default = Map;
156
183
  // Annotate the CommonJS export names for ESM import in node:
@@ -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\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 selectedMarkerId = null,\r\n onMarkerClick,\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 markersRef = useRef<any[]>([]);\r\n const [mapLib, setMapLib] = useState<any>(null);\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\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 });\r\n\r\n return () => {\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, index) => {\r\n const isSelected = markerData.id === selectedMarkerId;\r\n const el = createCustomMarkerElement({\r\n marker: markerData,\r\n isSelected: isSelected,\r\n });\r\n\r\n \r\n const marker = new mapLib.Marker({ \r\n element: el,\r\n anchor: 'bottom' \r\n })\r\n .setLngLat([markerData.lng, markerData.lat])\r\n .addTo(mapInstance);\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 }, [markers, mapInstance, selectedMarkerId]); \r\n\r\n return (\r\n <div style={{ position: 'relative', ...style }} className={className}>\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 const wrapper = document.createElement('div');\r\n \r\n // استایل‌های پایه بصورت Inline برای اطمینان از نمایش\r\n Object.assign(wrapper.style, {\r\n display: 'flex',\r\n flexDirection: 'column',\r\n alignItems: 'center',\r\n cursor: 'pointer',\r\n transition: 'all 0.3s ease',\r\n transform: isSelected ? 'scale(1.2)' : 'scale(1)',\r\n zIndex: isSelected ? '10' : '1'\r\n });\r\n\r\n // بخش تصویر مارکر\r\n const iconTarget = document.createElement('div');\r\n Object.assign(iconTarget.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 6px rgba(0,0,0,0.1)',\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 = '80%';\r\n img.style.height = '80%';\r\n img.style.objectFit = 'contain';\r\n iconTarget.appendChild(img);\r\n } else {\r\n // یک دایره رنگی ساده اگر لوگو نبود\r\n iconTarget.style.backgroundColor = '#ef4444';\r\n }\r\n\r\n wrapper.appendChild(iconTarget);\r\n\r\n // بخش برچسب قیمت\r\n if (showPrice && (marker.name || marker.price)) {\r\n const label = document.createElement('div');\r\n Object.assign(label.style, {\r\n backgroundColor: 'white',\r\n padding: '2px 8px',\r\n borderRadius: '4px',\r\n marginTop: '4px',\r\n fontSize: '11px',\r\n fontWeight: 'bold',\r\n whiteSpace: 'nowrap',\r\n boxShadow: '0 2px 4px rgba(0,0,0,0.2)',\r\n border: '1px solid #ddd'\r\n });\r\n const labelText = marker.price \r\n ? `${marker.price} ت` \r\n : marker.name;\r\n\r\nlabel.textContent = labelText || '';\r\n }\r\n\r\n return wrapper;\r\n}"],"mappings":"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","names":[]}
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":[]}
package/dist/index.css CHANGED
@@ -1,38 +1,40 @@
1
1
  /* src/styles.css */
2
- .neshan-map-container * {
3
- box-sizing: border-box;
2
+ .neshan-marker-container {
3
+ pointer-events: auto;
4
+ will-change: auto;
4
5
  }
5
- .neshan-marker {
6
- transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
7
- will-change: transform;
6
+ .neshan-marker-body {
7
+ cursor: pointer;
8
+ transform-origin: bottom center;
8
9
  }
9
- .neshan-marker:hover {
10
+ .neshan-marker-body:hover {
10
11
  transform: scale(1.2) !important;
12
+ z-index: 999;
11
13
  }
12
14
  .neshan-marker-label {
13
15
  background: white;
14
16
  color: #1f2937;
15
- font-size: 10px;
17
+ font-size: 11px;
16
18
  font-weight: bold;
17
- padding: 4px 8px;
18
- border-radius: 6px;
19
+ padding: 3px 8px;
20
+ border-radius: 4px;
19
21
  margin-top: 4px;
20
- box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
22
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
21
23
  white-space: nowrap;
22
- pointer-events: none;
24
+ border: 1px solid #e5e7eb;
23
25
  }
24
- .neshan-marker-selected {
25
- animation: pulse 2s infinite;
26
+ .neshan-marker-selected .neshan-marker-body {
27
+ animation: marker-pulse 1.5s infinite ease-in-out;
26
28
  }
27
- @keyframes pulse {
29
+ @keyframes marker-pulse {
28
30
  0% {
29
- transform: scale(1);
31
+ transform: scale(1.2);
30
32
  }
31
33
  50% {
32
- transform: scale(1.15);
34
+ transform: scale(1.3);
33
35
  }
34
36
  100% {
35
- transform: scale(1);
37
+ transform: scale(1.2);
36
38
  }
37
39
  }
38
40
  /*# sourceMappingURL=index.css.map */
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/styles.css"],"sourcesContent":["\r\n\r\n\r\n.neshan-map-container * {\r\n box-sizing: border-box;\r\n}\r\n\r\n\r\n.neshan-marker {\r\n transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);\r\n will-change: transform;\r\n}\r\n\r\n.neshan-marker:hover {\r\n transform: scale(1.2) !important;\r\n}\r\n\r\n\r\n.neshan-marker-label {\r\n background: white;\r\n color: #1f2937;\r\n font-size: 10px;\r\n font-weight: bold;\r\n padding: 4px 8px;\r\n border-radius: 6px;\r\n margin-top: 4px;\r\n box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);\r\n white-space: nowrap;\r\n pointer-events: none;\r\n}\r\n\r\n\r\n.neshan-marker-selected {\r\n animation: pulse 2s infinite;\r\n}\r\n\r\n@keyframes pulse {\r\n 0% {\r\n transform: scale(1);\r\n }\r\n 50% {\r\n transform: scale(1.15);\r\n }\r\n 100% {\r\n transform: scale(1);\r\n }\r\n}"],"mappings":";AAGA,CAAC,qBAAqB;AACpB,cAAY;AACd;AAGA,CAAC;AACC,cAAY,UAAU,KAAK,aAAa,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE;AACrD,eAAa;AACf;AAEA,CALC,aAKa;AACZ,aAAW,MAAM;AACnB;AAGA,CAAC;AACC,cAAY;AACZ,SAAO;AACP,aAAW;AACX,eAAa;AACb,WAAS,IAAI;AACb,iBAAe;AACf,cAAY;AACZ,cAAY,EAAE,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;AACpC,eAAa;AACb,kBAAgB;AAClB;AAGA,CAAC;AACC,aAAW,MAAM,GAAG;AACtB;AAEA,WAHa;AAIX;AACE,eAAW,MAAM;AACnB;AACA;AACE,eAAW,MAAM;AACnB;AACA;AACE,eAAW,MAAM;AACnB;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/styles.css"],"sourcesContent":["\r\n.neshan-marker-container {\r\n pointer-events: auto;\r\n will-change: auto;\r\n}\r\n\r\n\r\n.neshan-marker-body {\r\n cursor: pointer;\r\n transform-origin: bottom center;\r\n}\r\n\r\n.neshan-marker-body:hover {\r\n transform: scale(1.2) !important;\r\n z-index: 999;\r\n}\r\n\r\n.neshan-marker-label {\r\n background: white;\r\n color: #1f2937;\r\n font-size: 11px;\r\n font-weight: bold;\r\n padding: 3px 8px;\r\n border-radius: 4px;\r\n margin-top: 4px;\r\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);\r\n white-space: nowrap;\r\n border: 1px solid #e5e7eb;\r\n}\r\n\r\n\r\n.neshan-marker-selected .neshan-marker-body {\r\n animation: marker-pulse 1.5s infinite ease-in-out;\r\n}\r\n\r\n@keyframes marker-pulse {\r\n 0% { transform: scale(1.2); }\r\n 50% { transform: scale(1.3); }\r\n 100% { transform: scale(1.2); }\r\n}"],"mappings":";AACA,CAAC;AACC,kBAAgB;AAChB,eAAa;AACf;AAGA,CAAC;AACC,UAAQ;AACR,oBAAkB,OAAO;AAC3B;AAEA,CALC,kBAKkB;AACjB,aAAW,MAAM;AACjB,WAAS;AACX;AAEA,CAAC;AACC,cAAY;AACZ,SAAO;AACP,aAAW;AACX,eAAa;AACb,WAAS,IAAI;AACb,iBAAe;AACf,cAAY;AACZ,cAAY,EAAE,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;AACpC,eAAa;AACb,UAAQ,IAAI,MAAM;AACpB;AAGA,CAAC,uBAAuB,CAxBvB;AAyBC,aAAW,aAAa,KAAK,SAAS;AACxC;AAEA,WAHa;AAIX;AAAK,eAAW,MAAM;AAAM;AAC5B;AAAM,eAAW,MAAM;AAAM;AAC7B;AAAO,eAAW,MAAM;AAAM;AAChC;","names":[]}
package/dist/index.js CHANGED
@@ -8,24 +8,25 @@ function createCustomMarkerElement({
8
8
  logoSrc = "",
9
9
  showPrice = true
10
10
  }) {
11
- const wrapper = document.createElement("div");
12
- Object.assign(wrapper.style, {
11
+ const container = document.createElement("div");
12
+ container.className = "neshan-marker-container";
13
+ const body = document.createElement("div");
14
+ body.className = `neshan-marker-body ${isSelected ? "neshan-marker-selected" : ""}`;
15
+ Object.assign(body.style, {
13
16
  display: "flex",
14
17
  flexDirection: "column",
15
18
  alignItems: "center",
16
- cursor: "pointer",
17
- transition: "all 0.3s ease",
18
- transform: isSelected ? "scale(1.2)" : "scale(1)",
19
- zIndex: isSelected ? "10" : "1"
19
+ transition: "transform 0.3s ease",
20
+ transform: isSelected ? "scale(1.2)" : "scale(1)"
20
21
  });
21
- const iconTarget = document.createElement("div");
22
- Object.assign(iconTarget.style, {
22
+ const iconBox = document.createElement("div");
23
+ Object.assign(iconBox.style, {
23
24
  width: "40px",
24
25
  height: "40px",
25
26
  borderRadius: "50%",
26
27
  backgroundColor: "white",
27
28
  border: isSelected ? "3px solid #3b82f6" : "2px solid #ef4444",
28
- boxShadow: "0 4px 6px rgba(0,0,0,0.1)",
29
+ boxShadow: "0 4px 8px rgba(0,0,0,0.2)",
29
30
  display: "flex",
30
31
  alignItems: "center",
31
32
  justifyContent: "center",
@@ -34,31 +35,22 @@ function createCustomMarkerElement({
34
35
  if (logoSrc) {
35
36
  const img = document.createElement("img");
36
37
  img.src = logoSrc;
37
- img.style.width = "80%";
38
- img.style.height = "80%";
39
- img.style.objectFit = "contain";
40
- iconTarget.appendChild(img);
38
+ img.style.width = "100%";
39
+ img.style.height = "100%";
40
+ img.style.objectFit = "cover";
41
+ iconBox.appendChild(img);
41
42
  } else {
42
- iconTarget.style.backgroundColor = "#ef4444";
43
+ iconBox.style.backgroundColor = "#ef4444";
43
44
  }
44
- wrapper.appendChild(iconTarget);
45
- if (showPrice && (marker.name || marker.price)) {
45
+ body.appendChild(iconBox);
46
+ if (showPrice && (marker.price || marker.name)) {
46
47
  const label = document.createElement("div");
47
- Object.assign(label.style, {
48
- backgroundColor: "white",
49
- padding: "2px 8px",
50
- borderRadius: "4px",
51
- marginTop: "4px",
52
- fontSize: "11px",
53
- fontWeight: "bold",
54
- whiteSpace: "nowrap",
55
- boxShadow: "0 2px 4px rgba(0,0,0,0.2)",
56
- border: "1px solid #ddd"
57
- });
58
- const labelText = marker.price ? `${marker.price} \u062A` : marker.name;
59
- label.textContent = labelText || "";
48
+ label.className = "neshan-marker-label";
49
+ label.textContent = marker.price ? `${marker.price} \u062A` : marker.name ?? "";
50
+ body.appendChild(label);
60
51
  }
61
- return wrapper;
52
+ container.appendChild(body);
53
+ return container;
62
54
  }
63
55
 
64
56
  // src/components/Map.tsx
@@ -67,19 +59,27 @@ import { jsx } from "react/jsx-runtime";
67
59
  var Map = ({
68
60
  options,
69
61
  markers = [],
62
+ markerLogoUrl = "",
70
63
  selectedMarkerId = null,
71
64
  onMarkerClick,
65
+ onMapLoad,
72
66
  className = "",
73
67
  style = { width: "100%", height: "100%" }
74
68
  }) => {
75
69
  const mapContainerRef = useRef(null);
76
70
  const [mapInstance, setMapInstance] = useState(null);
77
- const markersRef = useRef([]);
78
71
  const [mapLib, setMapLib] = useState(null);
72
+ const markersRef = useRef([]);
79
73
  useEffect(() => {
74
+ let isMounted = true;
80
75
  import("@neshan-maps-platform/mapbox-gl").then((mod) => {
81
- setMapLib(mod.default || mod);
76
+ if (isMounted) {
77
+ setMapLib(mod.default || mod);
78
+ }
82
79
  });
80
+ return () => {
81
+ isMounted = false;
82
+ };
83
83
  }, []);
84
84
  useEffect(() => {
85
85
  if (!mapLib || !mapContainerRef.current || mapInstance) return;
@@ -89,8 +89,13 @@ var Map = ({
89
89
  });
90
90
  map.on("load", () => {
91
91
  setMapInstance(map);
92
+ onMapLoad?.(map);
92
93
  });
93
94
  return () => {
95
+ if (map) {
96
+ map.remove();
97
+ setMapInstance(null);
98
+ }
94
99
  };
95
100
  }, [mapLib]);
96
101
  useEffect(() => {
@@ -101,11 +106,13 @@ var Map = ({
101
106
  const isSelected = markerData.id === selectedMarkerId;
102
107
  const el = createCustomMarkerElement({
103
108
  marker: markerData,
104
- isSelected
109
+ isSelected,
110
+ logoSrc: markerLogoUrl
105
111
  });
106
112
  const marker = new mapLib.Marker({
107
113
  element: el,
108
- anchor: "bottom"
114
+ anchor: "bottom",
115
+ offset: [0, 0]
109
116
  }).setLngLat([markerData.lng, markerData.lat]).addTo(mapInstance);
110
117
  el.addEventListener("click", (e) => {
111
118
  e.stopPropagation();
@@ -113,8 +120,28 @@ var Map = ({
113
120
  });
114
121
  markersRef.current.push(marker);
115
122
  });
116
- }, [markers, mapInstance, selectedMarkerId]);
117
- return /* @__PURE__ */ jsx("div", { style: { position: "relative", ...style }, className, children: /* @__PURE__ */ jsx("div", { ref: mapContainerRef, style: { width: "100%", height: "100%" } }) });
123
+ return () => {
124
+ markersRef.current.forEach((m) => m.remove());
125
+ };
126
+ }, [markers, mapInstance, mapLib, selectedMarkerId, markerLogoUrl, onMarkerClick]);
127
+ return /* @__PURE__ */ jsx(
128
+ "div",
129
+ {
130
+ style: {
131
+ position: "relative",
132
+ overflow: "hidden",
133
+ ...style
134
+ },
135
+ className: `neshan-map-wrapper ${className}`,
136
+ children: /* @__PURE__ */ jsx(
137
+ "div",
138
+ {
139
+ ref: mapContainerRef,
140
+ style: { width: "100%", height: "100%" }
141
+ }
142
+ )
143
+ }
144
+ );
118
145
  };
119
146
  var Map_default = Map;
120
147
  export {
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\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 selectedMarkerId = null,\r\n onMarkerClick,\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 markersRef = useRef<any[]>([]);\r\n const [mapLib, setMapLib] = useState<any>(null);\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\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 });\r\n\r\n return () => {\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, index) => {\r\n const isSelected = markerData.id === selectedMarkerId;\r\n const el = createCustomMarkerElement({\r\n marker: markerData,\r\n isSelected: isSelected,\r\n });\r\n\r\n \r\n const marker = new mapLib.Marker({ \r\n element: el,\r\n anchor: 'bottom' \r\n })\r\n .setLngLat([markerData.lng, markerData.lat])\r\n .addTo(mapInstance);\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 }, [markers, mapInstance, selectedMarkerId]); \r\n\r\n return (\r\n <div style={{ position: 'relative', ...style }} className={className}>\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 const wrapper = document.createElement('div');\r\n \r\n // استایل‌های پایه بصورت Inline برای اطمینان از نمایش\r\n Object.assign(wrapper.style, {\r\n display: 'flex',\r\n flexDirection: 'column',\r\n alignItems: 'center',\r\n cursor: 'pointer',\r\n transition: 'all 0.3s ease',\r\n transform: isSelected ? 'scale(1.2)' : 'scale(1)',\r\n zIndex: isSelected ? '10' : '1'\r\n });\r\n\r\n // بخش تصویر مارکر\r\n const iconTarget = document.createElement('div');\r\n Object.assign(iconTarget.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 6px rgba(0,0,0,0.1)',\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 = '80%';\r\n img.style.height = '80%';\r\n img.style.objectFit = 'contain';\r\n iconTarget.appendChild(img);\r\n } else {\r\n // یک دایره رنگی ساده اگر لوگو نبود\r\n iconTarget.style.backgroundColor = '#ef4444';\r\n }\r\n\r\n wrapper.appendChild(iconTarget);\r\n\r\n // بخش برچسب قیمت\r\n if (showPrice && (marker.name || marker.price)) {\r\n const label = document.createElement('div');\r\n Object.assign(label.style, {\r\n backgroundColor: 'white',\r\n padding: '2px 8px',\r\n borderRadius: '4px',\r\n marginTop: '4px',\r\n fontSize: '11px',\r\n fontWeight: 'bold',\r\n whiteSpace: 'nowrap',\r\n boxShadow: '0 2px 4px rgba(0,0,0,0.2)',\r\n border: '1px solid #ddd'\r\n });\r\n const labelText = marker.price \r\n ? `${marker.price} ت` \r\n : marker.name;\r\n\r\nlabel.textContent = labelText || '';\r\n }\r\n\r\n return wrapper;\r\n}"],"mappings":"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","names":[]}
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":[]}
package/dist/react.cjs CHANGED
@@ -45,24 +45,25 @@ function createCustomMarkerElement({
45
45
  logoSrc = "",
46
46
  showPrice = true
47
47
  }) {
48
- const wrapper = document.createElement("div");
49
- Object.assign(wrapper.style, {
48
+ const container = document.createElement("div");
49
+ container.className = "neshan-marker-container";
50
+ const body = document.createElement("div");
51
+ body.className = `neshan-marker-body ${isSelected ? "neshan-marker-selected" : ""}`;
52
+ Object.assign(body.style, {
50
53
  display: "flex",
51
54
  flexDirection: "column",
52
55
  alignItems: "center",
53
- cursor: "pointer",
54
- transition: "all 0.3s ease",
55
- transform: isSelected ? "scale(1.2)" : "scale(1)",
56
- zIndex: isSelected ? "10" : "1"
56
+ transition: "transform 0.3s ease",
57
+ transform: isSelected ? "scale(1.2)" : "scale(1)"
57
58
  });
58
- const iconTarget = document.createElement("div");
59
- Object.assign(iconTarget.style, {
59
+ const iconBox = document.createElement("div");
60
+ Object.assign(iconBox.style, {
60
61
  width: "40px",
61
62
  height: "40px",
62
63
  borderRadius: "50%",
63
64
  backgroundColor: "white",
64
65
  border: isSelected ? "3px solid #3b82f6" : "2px solid #ef4444",
65
- boxShadow: "0 4px 6px rgba(0,0,0,0.1)",
66
+ boxShadow: "0 4px 8px rgba(0,0,0,0.2)",
66
67
  display: "flex",
67
68
  alignItems: "center",
68
69
  justifyContent: "center",
@@ -71,31 +72,22 @@ function createCustomMarkerElement({
71
72
  if (logoSrc) {
72
73
  const img = document.createElement("img");
73
74
  img.src = logoSrc;
74
- img.style.width = "80%";
75
- img.style.height = "80%";
76
- img.style.objectFit = "contain";
77
- iconTarget.appendChild(img);
75
+ img.style.width = "100%";
76
+ img.style.height = "100%";
77
+ img.style.objectFit = "cover";
78
+ iconBox.appendChild(img);
78
79
  } else {
79
- iconTarget.style.backgroundColor = "#ef4444";
80
+ iconBox.style.backgroundColor = "#ef4444";
80
81
  }
81
- wrapper.appendChild(iconTarget);
82
- if (showPrice && (marker.name || marker.price)) {
82
+ body.appendChild(iconBox);
83
+ if (showPrice && (marker.price || marker.name)) {
83
84
  const label = document.createElement("div");
84
- Object.assign(label.style, {
85
- backgroundColor: "white",
86
- padding: "2px 8px",
87
- borderRadius: "4px",
88
- marginTop: "4px",
89
- fontSize: "11px",
90
- fontWeight: "bold",
91
- whiteSpace: "nowrap",
92
- boxShadow: "0 2px 4px rgba(0,0,0,0.2)",
93
- border: "1px solid #ddd"
94
- });
95
- const labelText = marker.price ? `${marker.price} \u062A` : marker.name;
96
- label.textContent = labelText || "";
85
+ label.className = "neshan-marker-label";
86
+ label.textContent = marker.price ? `${marker.price} \u062A` : marker.name ?? "";
87
+ body.appendChild(label);
97
88
  }
98
- return wrapper;
89
+ container.appendChild(body);
90
+ return container;
99
91
  }
100
92
 
101
93
  // src/components/Map.tsx
@@ -104,19 +96,27 @@ var import_jsx_runtime = require("react/jsx-runtime");
104
96
  var Map = ({
105
97
  options,
106
98
  markers = [],
99
+ markerLogoUrl = "",
107
100
  selectedMarkerId = null,
108
101
  onMarkerClick,
102
+ onMapLoad,
109
103
  className = "",
110
104
  style = { width: "100%", height: "100%" }
111
105
  }) => {
112
106
  const mapContainerRef = (0, import_react.useRef)(null);
113
107
  const [mapInstance, setMapInstance] = (0, import_react.useState)(null);
114
- const markersRef = (0, import_react.useRef)([]);
115
108
  const [mapLib, setMapLib] = (0, import_react.useState)(null);
109
+ const markersRef = (0, import_react.useRef)([]);
116
110
  (0, import_react.useEffect)(() => {
111
+ let isMounted = true;
117
112
  import("@neshan-maps-platform/mapbox-gl").then((mod) => {
118
- setMapLib(mod.default || mod);
113
+ if (isMounted) {
114
+ setMapLib(mod.default || mod);
115
+ }
119
116
  });
117
+ return () => {
118
+ isMounted = false;
119
+ };
120
120
  }, []);
121
121
  (0, import_react.useEffect)(() => {
122
122
  if (!mapLib || !mapContainerRef.current || mapInstance) return;
@@ -126,8 +126,13 @@ var Map = ({
126
126
  });
127
127
  map.on("load", () => {
128
128
  setMapInstance(map);
129
+ onMapLoad?.(map);
129
130
  });
130
131
  return () => {
132
+ if (map) {
133
+ map.remove();
134
+ setMapInstance(null);
135
+ }
131
136
  };
132
137
  }, [mapLib]);
133
138
  (0, import_react.useEffect)(() => {
@@ -138,11 +143,13 @@ var Map = ({
138
143
  const isSelected = markerData.id === selectedMarkerId;
139
144
  const el = createCustomMarkerElement({
140
145
  marker: markerData,
141
- isSelected
146
+ isSelected,
147
+ logoSrc: markerLogoUrl
142
148
  });
143
149
  const marker = new mapLib.Marker({
144
150
  element: el,
145
- anchor: "bottom"
151
+ anchor: "bottom",
152
+ offset: [0, 0]
146
153
  }).setLngLat([markerData.lng, markerData.lat]).addTo(mapInstance);
147
154
  el.addEventListener("click", (e) => {
148
155
  e.stopPropagation();
@@ -150,8 +157,28 @@ var Map = ({
150
157
  });
151
158
  markersRef.current.push(marker);
152
159
  });
153
- }, [markers, mapInstance, selectedMarkerId]);
154
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { position: "relative", ...style }, className, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref: mapContainerRef, style: { width: "100%", height: "100%" } }) });
160
+ return () => {
161
+ markersRef.current.forEach((m) => m.remove());
162
+ };
163
+ }, [markers, mapInstance, mapLib, selectedMarkerId, markerLogoUrl, onMarkerClick]);
164
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
165
+ "div",
166
+ {
167
+ style: {
168
+ position: "relative",
169
+ overflow: "hidden",
170
+ ...style
171
+ },
172
+ className: `neshan-map-wrapper ${className}`,
173
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
174
+ "div",
175
+ {
176
+ ref: mapContainerRef,
177
+ style: { width: "100%", height: "100%" }
178
+ }
179
+ )
180
+ }
181
+ );
155
182
  };
156
183
  var Map_default = Map;
157
184
  // Annotate the CommonJS export names for ESM import in node:
@@ -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\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 selectedMarkerId = null,\r\n onMarkerClick,\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 markersRef = useRef<any[]>([]);\r\n const [mapLib, setMapLib] = useState<any>(null);\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\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 });\r\n\r\n return () => {\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, index) => {\r\n const isSelected = markerData.id === selectedMarkerId;\r\n const el = createCustomMarkerElement({\r\n marker: markerData,\r\n isSelected: isSelected,\r\n });\r\n\r\n \r\n const marker = new mapLib.Marker({ \r\n element: el,\r\n anchor: 'bottom' \r\n })\r\n .setLngLat([markerData.lng, markerData.lat])\r\n .addTo(mapInstance);\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 }, [markers, mapInstance, selectedMarkerId]); \r\n\r\n return (\r\n <div style={{ position: 'relative', ...style }} className={className}>\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 const wrapper = document.createElement('div');\r\n \r\n // استایل‌های پایه بصورت Inline برای اطمینان از نمایش\r\n Object.assign(wrapper.style, {\r\n display: 'flex',\r\n flexDirection: 'column',\r\n alignItems: 'center',\r\n cursor: 'pointer',\r\n transition: 'all 0.3s ease',\r\n transform: isSelected ? 'scale(1.2)' : 'scale(1)',\r\n zIndex: isSelected ? '10' : '1'\r\n });\r\n\r\n // بخش تصویر مارکر\r\n const iconTarget = document.createElement('div');\r\n Object.assign(iconTarget.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 6px rgba(0,0,0,0.1)',\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 = '80%';\r\n img.style.height = '80%';\r\n img.style.objectFit = 'contain';\r\n iconTarget.appendChild(img);\r\n } else {\r\n // یک دایره رنگی ساده اگر لوگو نبود\r\n iconTarget.style.backgroundColor = '#ef4444';\r\n }\r\n\r\n wrapper.appendChild(iconTarget);\r\n\r\n // بخش برچسب قیمت\r\n if (showPrice && (marker.name || marker.price)) {\r\n const label = document.createElement('div');\r\n Object.assign(label.style, {\r\n backgroundColor: 'white',\r\n padding: '2px 8px',\r\n borderRadius: '4px',\r\n marginTop: '4px',\r\n fontSize: '11px',\r\n fontWeight: 'bold',\r\n whiteSpace: 'nowrap',\r\n boxShadow: '0 2px 4px rgba(0,0,0,0.2)',\r\n border: '1px solid #ddd'\r\n });\r\n const labelText = marker.price \r\n ? `${marker.price} ت` \r\n : marker.name;\r\n\r\nlabel.textContent = labelText || '';\r\n }\r\n\r\n return wrapper;\r\n}"],"mappings":"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","names":[]}
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":[]}
package/dist/react.js CHANGED
@@ -10,24 +10,25 @@ function createCustomMarkerElement({
10
10
  logoSrc = "",
11
11
  showPrice = true
12
12
  }) {
13
- const wrapper = document.createElement("div");
14
- Object.assign(wrapper.style, {
13
+ const container = document.createElement("div");
14
+ container.className = "neshan-marker-container";
15
+ const body = document.createElement("div");
16
+ body.className = `neshan-marker-body ${isSelected ? "neshan-marker-selected" : ""}`;
17
+ Object.assign(body.style, {
15
18
  display: "flex",
16
19
  flexDirection: "column",
17
20
  alignItems: "center",
18
- cursor: "pointer",
19
- transition: "all 0.3s ease",
20
- transform: isSelected ? "scale(1.2)" : "scale(1)",
21
- zIndex: isSelected ? "10" : "1"
21
+ transition: "transform 0.3s ease",
22
+ transform: isSelected ? "scale(1.2)" : "scale(1)"
22
23
  });
23
- const iconTarget = document.createElement("div");
24
- Object.assign(iconTarget.style, {
24
+ const iconBox = document.createElement("div");
25
+ Object.assign(iconBox.style, {
25
26
  width: "40px",
26
27
  height: "40px",
27
28
  borderRadius: "50%",
28
29
  backgroundColor: "white",
29
30
  border: isSelected ? "3px solid #3b82f6" : "2px solid #ef4444",
30
- boxShadow: "0 4px 6px rgba(0,0,0,0.1)",
31
+ boxShadow: "0 4px 8px rgba(0,0,0,0.2)",
31
32
  display: "flex",
32
33
  alignItems: "center",
33
34
  justifyContent: "center",
@@ -36,31 +37,22 @@ function createCustomMarkerElement({
36
37
  if (logoSrc) {
37
38
  const img = document.createElement("img");
38
39
  img.src = logoSrc;
39
- img.style.width = "80%";
40
- img.style.height = "80%";
41
- img.style.objectFit = "contain";
42
- iconTarget.appendChild(img);
40
+ img.style.width = "100%";
41
+ img.style.height = "100%";
42
+ img.style.objectFit = "cover";
43
+ iconBox.appendChild(img);
43
44
  } else {
44
- iconTarget.style.backgroundColor = "#ef4444";
45
+ iconBox.style.backgroundColor = "#ef4444";
45
46
  }
46
- wrapper.appendChild(iconTarget);
47
- if (showPrice && (marker.name || marker.price)) {
47
+ body.appendChild(iconBox);
48
+ if (showPrice && (marker.price || marker.name)) {
48
49
  const label = document.createElement("div");
49
- Object.assign(label.style, {
50
- backgroundColor: "white",
51
- padding: "2px 8px",
52
- borderRadius: "4px",
53
- marginTop: "4px",
54
- fontSize: "11px",
55
- fontWeight: "bold",
56
- whiteSpace: "nowrap",
57
- boxShadow: "0 2px 4px rgba(0,0,0,0.2)",
58
- border: "1px solid #ddd"
59
- });
60
- const labelText = marker.price ? `${marker.price} \u062A` : marker.name;
61
- label.textContent = labelText || "";
50
+ label.className = "neshan-marker-label";
51
+ label.textContent = marker.price ? `${marker.price} \u062A` : marker.name ?? "";
52
+ body.appendChild(label);
62
53
  }
63
- return wrapper;
54
+ container.appendChild(body);
55
+ return container;
64
56
  }
65
57
 
66
58
  // src/components/Map.tsx
@@ -69,19 +61,27 @@ import { jsx } from "react/jsx-runtime";
69
61
  var Map = ({
70
62
  options,
71
63
  markers = [],
64
+ markerLogoUrl = "",
72
65
  selectedMarkerId = null,
73
66
  onMarkerClick,
67
+ onMapLoad,
74
68
  className = "",
75
69
  style = { width: "100%", height: "100%" }
76
70
  }) => {
77
71
  const mapContainerRef = useRef(null);
78
72
  const [mapInstance, setMapInstance] = useState(null);
79
- const markersRef = useRef([]);
80
73
  const [mapLib, setMapLib] = useState(null);
74
+ const markersRef = useRef([]);
81
75
  useEffect(() => {
76
+ let isMounted = true;
82
77
  import("@neshan-maps-platform/mapbox-gl").then((mod) => {
83
- setMapLib(mod.default || mod);
78
+ if (isMounted) {
79
+ setMapLib(mod.default || mod);
80
+ }
84
81
  });
82
+ return () => {
83
+ isMounted = false;
84
+ };
85
85
  }, []);
86
86
  useEffect(() => {
87
87
  if (!mapLib || !mapContainerRef.current || mapInstance) return;
@@ -91,8 +91,13 @@ var Map = ({
91
91
  });
92
92
  map.on("load", () => {
93
93
  setMapInstance(map);
94
+ onMapLoad?.(map);
94
95
  });
95
96
  return () => {
97
+ if (map) {
98
+ map.remove();
99
+ setMapInstance(null);
100
+ }
96
101
  };
97
102
  }, [mapLib]);
98
103
  useEffect(() => {
@@ -103,11 +108,13 @@ var Map = ({
103
108
  const isSelected = markerData.id === selectedMarkerId;
104
109
  const el = createCustomMarkerElement({
105
110
  marker: markerData,
106
- isSelected
111
+ isSelected,
112
+ logoSrc: markerLogoUrl
107
113
  });
108
114
  const marker = new mapLib.Marker({
109
115
  element: el,
110
- anchor: "bottom"
116
+ anchor: "bottom",
117
+ offset: [0, 0]
111
118
  }).setLngLat([markerData.lng, markerData.lat]).addTo(mapInstance);
112
119
  el.addEventListener("click", (e) => {
113
120
  e.stopPropagation();
@@ -115,8 +122,28 @@ var Map = ({
115
122
  });
116
123
  markersRef.current.push(marker);
117
124
  });
118
- }, [markers, mapInstance, selectedMarkerId]);
119
- return /* @__PURE__ */ jsx("div", { style: { position: "relative", ...style }, className, children: /* @__PURE__ */ jsx("div", { ref: mapContainerRef, style: { width: "100%", height: "100%" } }) });
125
+ return () => {
126
+ markersRef.current.forEach((m) => m.remove());
127
+ };
128
+ }, [markers, mapInstance, mapLib, selectedMarkerId, markerLogoUrl, onMarkerClick]);
129
+ return /* @__PURE__ */ jsx(
130
+ "div",
131
+ {
132
+ style: {
133
+ position: "relative",
134
+ overflow: "hidden",
135
+ ...style
136
+ },
137
+ className: `neshan-map-wrapper ${className}`,
138
+ children: /* @__PURE__ */ jsx(
139
+ "div",
140
+ {
141
+ ref: mapContainerRef,
142
+ style: { width: "100%", height: "100%" }
143
+ }
144
+ )
145
+ }
146
+ );
120
147
  };
121
148
  var Map_default = Map;
122
149
  export {
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\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 selectedMarkerId = null,\r\n onMarkerClick,\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 markersRef = useRef<any[]>([]);\r\n const [mapLib, setMapLib] = useState<any>(null);\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\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 });\r\n\r\n return () => {\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, index) => {\r\n const isSelected = markerData.id === selectedMarkerId;\r\n const el = createCustomMarkerElement({\r\n marker: markerData,\r\n isSelected: isSelected,\r\n });\r\n\r\n \r\n const marker = new mapLib.Marker({ \r\n element: el,\r\n anchor: 'bottom' \r\n })\r\n .setLngLat([markerData.lng, markerData.lat])\r\n .addTo(mapInstance);\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 }, [markers, mapInstance, selectedMarkerId]); \r\n\r\n return (\r\n <div style={{ position: 'relative', ...style }} className={className}>\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 const wrapper = document.createElement('div');\r\n \r\n // استایل‌های پایه بصورت Inline برای اطمینان از نمایش\r\n Object.assign(wrapper.style, {\r\n display: 'flex',\r\n flexDirection: 'column',\r\n alignItems: 'center',\r\n cursor: 'pointer',\r\n transition: 'all 0.3s ease',\r\n transform: isSelected ? 'scale(1.2)' : 'scale(1)',\r\n zIndex: isSelected ? '10' : '1'\r\n });\r\n\r\n // بخش تصویر مارکر\r\n const iconTarget = document.createElement('div');\r\n Object.assign(iconTarget.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 6px rgba(0,0,0,0.1)',\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 = '80%';\r\n img.style.height = '80%';\r\n img.style.objectFit = 'contain';\r\n iconTarget.appendChild(img);\r\n } else {\r\n // یک دایره رنگی ساده اگر لوگو نبود\r\n iconTarget.style.backgroundColor = '#ef4444';\r\n }\r\n\r\n wrapper.appendChild(iconTarget);\r\n\r\n // بخش برچسب قیمت\r\n if (showPrice && (marker.name || marker.price)) {\r\n const label = document.createElement('div');\r\n Object.assign(label.style, {\r\n backgroundColor: 'white',\r\n padding: '2px 8px',\r\n borderRadius: '4px',\r\n marginTop: '4px',\r\n fontSize: '11px',\r\n fontWeight: 'bold',\r\n whiteSpace: 'nowrap',\r\n boxShadow: '0 2px 4px rgba(0,0,0,0.2)',\r\n border: '1px solid #ddd'\r\n });\r\n const labelText = marker.price \r\n ? `${marker.price} ت` \r\n : marker.name;\r\n\r\nlabel.textContent = labelText || '';\r\n }\r\n\r\n return wrapper;\r\n}"],"mappings":"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","names":[]}
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":";;;AAEA,SAAgB,WAAW,QAAQ,gBAAgB;;;ACO5C,SAAS,0BAA0B;AAAA,EACxC;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,YAAY;AACd,GAAqC;AAEnC,QAAM,YAAY,SAAS,cAAc,KAAK;AAC9C,YAAU,YAAY;AAGtB,QAAM,OAAO,SAAS,cAAc,KAAK;AACzC,OAAK,YAAY,sBAAsB,aAAa,2BAA2B,EAAE;AAGjF,SAAO,OAAO,KAAK,OAAO;AAAA,IACxB,SAAS;AAAA,IACT,eAAe;AAAA,IACf,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,WAAW,aAAa,eAAe;AAAA,EACzC,CAAC;AAGD,QAAM,UAAU,SAAS,cAAc,KAAK;AAC5C,SAAO,OAAO,QAAQ,OAAO;AAAA,IAC3B,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,cAAc;AAAA,IACd,iBAAiB;AAAA,IACjB,QAAQ,aAAa,sBAAsB;AAAA,IAC3C,WAAW;AAAA,IACX,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,UAAU;AAAA,EACZ,CAAC;AAED,MAAI,SAAS;AACX,UAAM,MAAM,SAAS,cAAc,KAAK;AACxC,QAAI,MAAM;AACV,QAAI,MAAM,QAAQ;AAClB,QAAI,MAAM,SAAS;AACnB,QAAI,MAAM,YAAY;AACtB,YAAQ,YAAY,GAAG;AAAA,EACzB,OAAO;AACL,YAAQ,MAAM,kBAAkB;AAAA,EAClC;AAEA,OAAK,YAAY,OAAO;AAGxB,MAAI,cAAc,OAAO,SAAS,OAAO,OAAO;AAC9C,UAAM,QAAQ,SAAS,cAAc,KAAK;AAC1C,UAAM,YAAY;AAClB,UAAM,cAAc,OAAO,QAAQ,GAAG,OAAO,KAAK,YAAQ,OAAO,QAAQ;AACzE,SAAK,YAAY,KAAK;AAAA,EACxB;AAEA,YAAU,YAAY,IAAI;AAC1B,SAAO;AACT;;;AD/DA,OAAO;AA4GD;AA1GN,IAAM,MAA0B,CAAC;AAAA,EAC/B;AAAA,EACA,UAAU,CAAC;AAAA,EACX,gBAAgB;AAAA,EAChB,mBAAmB;AAAA,EACnB;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ,QAAQ,EAAE,OAAO,QAAQ,QAAQ,OAAO;AAC1C,MAAM;AACJ,QAAM,kBAAkB,OAAuB,IAAI;AACnD,QAAM,CAAC,aAAa,cAAc,IAAI,SAA2B,IAAI;AACrE,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAc,IAAI;AAC9C,QAAM,aAAa,OAAc,CAAC,CAAC;AAGnC,YAAU,MAAM;AACd,QAAI,YAAY;AAEhB,WAAO,iCAAiC,EAAE,KAAK,CAAC,QAAQ;AACtD,UAAI,WAAW;AACb,kBAAU,IAAI,WAAW,GAAG;AAAA,MAC9B;AAAA,IACF,CAAC;AAED,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,CAAC;AAGL,YAAU,MAAM;AACd,QAAI,CAAC,UAAU,CAAC,gBAAgB,WAAW,YAAa;AAExD,UAAM,MAAM,IAAI,OAAO,IAAI;AAAA,MACzB,WAAW,gBAAgB;AAAA,MAC3B,GAAG;AAAA,IACL,CAAC;AAED,QAAI,GAAG,QAAQ,MAAM;AACnB,qBAAe,GAAG;AAClB,kBAAY,GAAG;AAAA,IACjB,CAAC;AAGD,WAAO,MAAM;AACX,UAAI,KAAK;AACP,YAAI,OAAO;AACX,uBAAe,IAAI;AAAA,MACrB;AAAA,IACF;AAAA,EAGF,GAAG,CAAC,MAAM,CAAC;AAGX,YAAU,MAAM;AACd,QAAI,CAAC,eAAe,CAAC,OAAQ;AAG7B,eAAW,QAAQ,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;AAC5C,eAAW,UAAU,CAAC;AAEtB,YAAQ,QAAQ,CAAC,YAAwB,UAAkB;AACzD,YAAM,aAAa,WAAW,OAAO;AAGrC,YAAM,KAAK,0BAA0B;AAAA,QACnC,QAAQ;AAAA,QACR;AAAA,QACA,SAAS;AAAA,MACX,CAAC;AAGD,YAAM,SAAS,IAAI,OAAO,OAAO;AAAA,QAC/B,SAAS;AAAA,QACT,QAAQ;AAAA,QACR,QAAQ,CAAC,GAAG,CAAC;AAAA,MACf,CAAC,EACE,UAAU,CAAC,WAAW,KAAK,WAAW,GAAG,CAAC,EAC1C,MAAM,WAAW;AAGpB,SAAG,iBAAiB,SAAS,CAAC,MAAM;AAClC,UAAE,gBAAgB;AAClB,wBAAgB,YAAY,OAAO,WAAW;AAAA,MAChD,CAAC;AAED,iBAAW,QAAQ,KAAK,MAAM;AAAA,IAChC,CAAC;AAGD,WAAO,MAAM;AACX,iBAAW,QAAQ,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;AAAA,IAC9C;AAAA,EACF,GAAG,CAAC,SAAS,aAAa,QAAQ,kBAAkB,eAAe,aAAa,CAAC;AAEjF,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAO;AAAA,QACL,UAAU;AAAA,QACV,UAAU;AAAA,QACV,GAAG;AAAA,MACL;AAAA,MACA,WAAW,sBAAsB,SAAS;AAAA,MAE1C;AAAA,QAAC;AAAA;AAAA,UACC,KAAK;AAAA,UACL,OAAO,EAAE,OAAO,QAAQ,QAAQ,OAAO;AAAA;AAAA,MACzC;AAAA;AAAA,EACF;AAEJ;AAEA,IAAO,cAAQ;","names":[]}
package/dist/styles.css CHANGED
@@ -1,47 +1,40 @@
1
1
 
2
-
3
-
4
- .neshan-map-container * {
5
- box-sizing: border-box;
2
+ .neshan-marker-container {
3
+ pointer-events: auto;
4
+ will-change: auto;
6
5
  }
7
6
 
8
7
 
9
- .neshan-marker {
10
- transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
11
- will-change: transform;
8
+ .neshan-marker-body {
9
+ cursor: pointer;
10
+ transform-origin: bottom center;
12
11
  }
13
12
 
14
- .neshan-marker:hover {
13
+ .neshan-marker-body:hover {
15
14
  transform: scale(1.2) !important;
15
+ z-index: 999;
16
16
  }
17
17
 
18
-
19
18
  .neshan-marker-label {
20
19
  background: white;
21
20
  color: #1f2937;
22
- font-size: 10px;
21
+ font-size: 11px;
23
22
  font-weight: bold;
24
- padding: 4px 8px;
25
- border-radius: 6px;
23
+ padding: 3px 8px;
24
+ border-radius: 4px;
26
25
  margin-top: 4px;
27
- box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
26
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
28
27
  white-space: nowrap;
29
- pointer-events: none;
28
+ border: 1px solid #e5e7eb;
30
29
  }
31
30
 
32
31
 
33
- .neshan-marker-selected {
34
- animation: pulse 2s infinite;
32
+ .neshan-marker-selected .neshan-marker-body {
33
+ animation: marker-pulse 1.5s infinite ease-in-out;
35
34
  }
36
35
 
37
- @keyframes pulse {
38
- 0% {
39
- transform: scale(1);
40
- }
41
- 50% {
42
- transform: scale(1.15);
43
- }
44
- 100% {
45
- transform: scale(1);
46
- }
36
+ @keyframes marker-pulse {
37
+ 0% { transform: scale(1.2); }
38
+ 50% { transform: scale(1.3); }
39
+ 100% { transform: scale(1.2); }
47
40
  }
@@ -1 +1 @@
1
- {"version":3,"file":"createCustomMarkerElement.d.ts","sourceRoot":"","sources":["../../src/utils/createCustomMarkerElement.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAE3C,UAAU,mBAAmB;IAC3B,MAAM,EAAE,UAAU,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,wBAAgB,yBAAyB,CAAC,EACxC,MAAM,EACN,UAAU,EACV,OAAY,EACZ,SAAgB,GACjB,EAAE,mBAAmB,GAAG,WAAW,CAiEnC"}
1
+ {"version":3,"file":"createCustomMarkerElement.d.ts","sourceRoot":"","sources":["../../src/utils/createCustomMarkerElement.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAE3C,UAAU,mBAAmB;IAC3B,MAAM,EAAE,UAAU,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,wBAAgB,yBAAyB,CAAC,EACxC,MAAM,EACN,UAAU,EACV,OAAY,EACZ,SAAgB,GACjB,EAAE,mBAAmB,GAAG,WAAW,CAwDnC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mehdi-akbari-map",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "description": "A professional Map",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",