@mamrp/components 1.7.54 → 1.7.56

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.mjs CHANGED
@@ -6163,6 +6163,426 @@ var UploadImage2 = ({
6163
6163
  );
6164
6164
  };
6165
6165
  var upload_image_default = UploadImage2;
6166
+
6167
+ // src/map-location-picker/index.tsx
6168
+ import {
6169
+ Box as Box27,
6170
+ IconButton as IconButton14,
6171
+ Paper as Paper2,
6172
+ Typography as Typography19,
6173
+ CircularProgress as CircularProgress11,
6174
+ useTheme as useTheme9,
6175
+ Button as Button15,
6176
+ Dialog as Dialog10,
6177
+ DialogTitle as DialogTitle8,
6178
+ DialogContent as DialogContent8,
6179
+ DialogActions as DialogActions5
6180
+ } from "@mui/material";
6181
+ import { MdMyLocation, MdOutlineClear as MdOutlineClear2 } from "react-icons/md";
6182
+ import { useEffect as useEffect12, useRef as useRef8, useState as useState17 } from "react";
6183
+ import toast2, { Toaster } from "react-hot-toast";
6184
+ import { useController } from "react-hook-form";
6185
+ function MapLocationPicker({
6186
+ control,
6187
+ nameLat,
6188
+ nameLong,
6189
+ nameAddress,
6190
+ isLoading = false,
6191
+ defaultLat = 36.557153,
6192
+ defaultLng = 52.679547,
6193
+ defaultZoom = 16,
6194
+ minZoom = 10,
6195
+ maxZoom = 19
6196
+ }) {
6197
+ const theme3 = useTheme9();
6198
+ const isDarkMode = theme3.palette.mode === "dark";
6199
+ const { field: latField, fieldState: latState } = useController({
6200
+ name: nameLat,
6201
+ control
6202
+ });
6203
+ const { field: longField, fieldState: longState } = useController({
6204
+ name: nameLong,
6205
+ control
6206
+ });
6207
+ const addressController = nameAddress ? useController({ name: nameAddress, control }) : null;
6208
+ const error = !!latState.error || !!longState.error;
6209
+ const helperText = latState.error?.message || longState.error?.message || "";
6210
+ const latitude = latField.value;
6211
+ const longitude = longField.value;
6212
+ const handleLocationChange = (lat, lng) => {
6213
+ latField.onChange(lat);
6214
+ longField.onChange(lng);
6215
+ };
6216
+ const handleAddressChange = nameAddress ? (address) => addressController?.field.onChange(address) : void 0;
6217
+ const mapRef = useRef8(null);
6218
+ const mapInstanceRef = useRef8(null);
6219
+ const markerRef = useRef8(null);
6220
+ const [isMapLoading, setIsMapLoading] = useState17(true);
6221
+ const [isGeocoding, setIsGeocoding] = useState17(false);
6222
+ const [isGeolocating, setIsGeolocating] = useState17(false);
6223
+ const [showGpsDialog, setShowGpsDialog] = useState17(false);
6224
+ const [gpsErrorType, setGpsErrorType] = useState17("other");
6225
+ const [zoom, setZoom] = useState17(defaultZoom);
6226
+ const hasExistingLocation = latitude !== null && longitude !== null;
6227
+ const initialLat = hasExistingLocation ? latitude : defaultLat;
6228
+ const initialLng = hasExistingLocation ? longitude : defaultLng;
6229
+ const initialZoom = hasExistingLocation ? 17 : defaultZoom;
6230
+ useEffect12(() => {
6231
+ if (!document.getElementById("leaflet-css")) {
6232
+ const link = document.createElement("link");
6233
+ link.id = "leaflet-css";
6234
+ link.rel = "stylesheet";
6235
+ link.href = "https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.css";
6236
+ document.head.appendChild(link);
6237
+ }
6238
+ const script = document.createElement("script");
6239
+ script.src = "https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.js";
6240
+ script.async = true;
6241
+ script.onload = () => {
6242
+ setIsMapLoading(false);
6243
+ initializeMap();
6244
+ };
6245
+ document.body.appendChild(script);
6246
+ return () => {
6247
+ if (mapInstanceRef.current) {
6248
+ mapInstanceRef.current.remove();
6249
+ mapInstanceRef.current = null;
6250
+ }
6251
+ };
6252
+ }, []);
6253
+ const reverseGeocode = async (lat, lng) => {
6254
+ try {
6255
+ const response = await fetch(
6256
+ `https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=${lat}&lon=${lng}&accept-language=fa`
6257
+ );
6258
+ if (!response.ok) throw new Error("Failed to fetch address");
6259
+ const data = await response.json();
6260
+ const fullAddress = data.display_name || "";
6261
+ const parts = fullAddress.split(",");
6262
+ return parts.slice(0, 4).map((p) => p.trim()).join("\u060C ");
6263
+ } catch {
6264
+ return "";
6265
+ }
6266
+ };
6267
+ const initializeMap = () => {
6268
+ if (!window.L || !mapRef.current || mapInstanceRef.current) return;
6269
+ const L = window.L;
6270
+ const map = L.map(mapRef.current, {
6271
+ attributionControl: false,
6272
+ minZoom
6273
+ }).setView([initialLat, initialLng], initialZoom);
6274
+ L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
6275
+ attribution: "\xA9 OpenStreetMap contributors",
6276
+ maxZoom
6277
+ }).addTo(map);
6278
+ map.on("zoomend", () => {
6279
+ setZoom(map.getZoom());
6280
+ });
6281
+ if (hasExistingLocation) {
6282
+ markerRef.current = L.marker([initialLat, initialLng], {
6283
+ draggable: true
6284
+ }).addTo(map);
6285
+ markerRef.current.on("dragend", async (e) => {
6286
+ const { lat, lng } = e.target.getLatLng();
6287
+ handleLocationChange(lat, lng);
6288
+ if (handleAddressChange) {
6289
+ setIsGeocoding(true);
6290
+ const address = await reverseGeocode(lat, lng);
6291
+ handleAddressChange(address);
6292
+ setIsGeocoding(false);
6293
+ }
6294
+ });
6295
+ }
6296
+ map.on("click", async (e) => {
6297
+ const { lat, lng } = e.latlng;
6298
+ if (!markerRef.current) {
6299
+ markerRef.current = L.marker([lat, lng], { draggable: true }).addTo(
6300
+ map
6301
+ );
6302
+ markerRef.current.on("dragend", async (dragEvent) => {
6303
+ const { lat: dragLat, lng: dragLng } = dragEvent.target.getLatLng();
6304
+ handleLocationChange(dragLat, dragLng);
6305
+ if (handleAddressChange) {
6306
+ setIsGeocoding(true);
6307
+ const address = await reverseGeocode(dragLat, dragLng);
6308
+ handleAddressChange(address);
6309
+ setIsGeocoding(false);
6310
+ }
6311
+ });
6312
+ } else {
6313
+ markerRef.current.setLatLng([lat, lng]);
6314
+ }
6315
+ handleLocationChange(lat, lng);
6316
+ if (handleAddressChange) {
6317
+ setIsGeocoding(true);
6318
+ const address = await reverseGeocode(lat, lng);
6319
+ handleAddressChange(address);
6320
+ setIsGeocoding(false);
6321
+ }
6322
+ });
6323
+ mapInstanceRef.current = map;
6324
+ };
6325
+ useEffect12(() => {
6326
+ if (!mapInstanceRef.current || !mapRef.current) return;
6327
+ const L = window.L;
6328
+ if (latitude === null || longitude === null) {
6329
+ if (markerRef.current) {
6330
+ markerRef.current.remove();
6331
+ markerRef.current = null;
6332
+ }
6333
+ return;
6334
+ }
6335
+ if (!markerRef.current) {
6336
+ markerRef.current = L.marker([latitude, longitude], {
6337
+ draggable: true
6338
+ }).addTo(mapInstanceRef.current);
6339
+ markerRef.current.on("dragend", async (e) => {
6340
+ const { lat, lng } = e.target.getLatLng();
6341
+ handleLocationChange(lat, lng);
6342
+ if (handleAddressChange) {
6343
+ setIsGeocoding(true);
6344
+ const address = await reverseGeocode(lat, lng);
6345
+ handleAddressChange(address);
6346
+ setIsGeocoding(false);
6347
+ }
6348
+ });
6349
+ } else {
6350
+ markerRef.current.setLatLng([latitude, longitude]);
6351
+ }
6352
+ mapInstanceRef.current.setView([latitude, longitude], zoom, {
6353
+ animate: true
6354
+ });
6355
+ }, [latitude, longitude]);
6356
+ const checkGeolocationSupport = () => {
6357
+ if (!navigator.geolocation) {
6358
+ toast2.error("\u0645\u0631\u0648\u0631\u06AF\u0631 \u0634\u0645\u0627 \u0627\u0632 \u0645\u0648\u0642\u0639\u06CC\u062A \u0645\u06A9\u0627\u0646\u06CC \u067E\u0634\u062A\u06CC\u0628\u0627\u0646\u06CC \u0646\u0645\u06CC\u200C\u06A9\u0646\u062F.");
6359
+ return false;
6360
+ }
6361
+ return true;
6362
+ };
6363
+ const handleCurrentLocation = async () => {
6364
+ if (!checkGeolocationSupport() || !mapInstanceRef.current) return;
6365
+ if (navigator.permissions) {
6366
+ try {
6367
+ const permissionStatus = await navigator.permissions.query({
6368
+ name: "geolocation"
6369
+ });
6370
+ if (permissionStatus.state === "denied") {
6371
+ setGpsErrorType("permission");
6372
+ setShowGpsDialog(true);
6373
+ return;
6374
+ }
6375
+ } catch {
6376
+ }
6377
+ }
6378
+ setIsGeolocating(true);
6379
+ const timeoutId = setTimeout(() => {
6380
+ if (isGeolocating) {
6381
+ setIsGeolocating(false);
6382
+ setGpsErrorType("gps_off");
6383
+ setShowGpsDialog(true);
6384
+ }
6385
+ }, 5e3);
6386
+ navigator.geolocation.getCurrentPosition(
6387
+ async (pos) => {
6388
+ clearTimeout(timeoutId);
6389
+ setIsGeolocating(false);
6390
+ const { latitude: lat, longitude: lng } = pos.coords;
6391
+ const map = mapInstanceRef.current;
6392
+ const L = window.L;
6393
+ handleLocationChange(lat, lng);
6394
+ if (!markerRef.current) {
6395
+ markerRef.current = L.marker([lat, lng], { draggable: true }).addTo(
6396
+ map
6397
+ );
6398
+ markerRef.current.on("dragend", async (e) => {
6399
+ const { lat: dragLat, lng: dragLng } = e.target.getLatLng();
6400
+ handleLocationChange(dragLat, dragLng);
6401
+ if (handleAddressChange) {
6402
+ setIsGeocoding(true);
6403
+ const address = await reverseGeocode(dragLat, dragLng);
6404
+ handleAddressChange(address);
6405
+ setIsGeocoding(false);
6406
+ }
6407
+ });
6408
+ } else {
6409
+ markerRef.current.setLatLng([lat, lng]);
6410
+ }
6411
+ map.setView([lat, lng], 17, { animate: true });
6412
+ if (handleAddressChange) {
6413
+ setIsGeocoding(true);
6414
+ const address = await reverseGeocode(lat, lng);
6415
+ handleAddressChange(address);
6416
+ setIsGeocoding(false);
6417
+ }
6418
+ toast2.success("\u0645\u0648\u0642\u0639\u06CC\u062A \u0645\u06A9\u0627\u0646\u06CC \u0628\u0627 \u0645\u0648\u0641\u0642\u06CC\u062A \u062F\u0631\u06CC\u0627\u0641\u062A \u0634\u062F");
6419
+ },
6420
+ (error2) => {
6421
+ clearTimeout(timeoutId);
6422
+ setIsGeolocating(false);
6423
+ switch (error2.code) {
6424
+ case error2.PERMISSION_DENIED:
6425
+ setGpsErrorType("permission");
6426
+ break;
6427
+ case error2.POSITION_UNAVAILABLE:
6428
+ case error2.TIMEOUT:
6429
+ setGpsErrorType("gps_off");
6430
+ break;
6431
+ default:
6432
+ setGpsErrorType("other");
6433
+ break;
6434
+ }
6435
+ setShowGpsDialog(true);
6436
+ },
6437
+ {
6438
+ enableHighAccuracy: true,
6439
+ timeout: 8e3,
6440
+ maximumAge: 0
6441
+ }
6442
+ );
6443
+ };
6444
+ const handleClearLocation = () => {
6445
+ if (markerRef.current) {
6446
+ markerRef.current.remove();
6447
+ markerRef.current = null;
6448
+ }
6449
+ handleLocationChange(null, null);
6450
+ if (handleAddressChange) handleAddressChange("");
6451
+ };
6452
+ const handleGpsDialogClose = () => setShowGpsDialog(false);
6453
+ const handleRetryLocation = () => {
6454
+ setShowGpsDialog(false);
6455
+ setTimeout(handleCurrentLocation, 500);
6456
+ };
6457
+ const handleOpenSettings = () => {
6458
+ setShowGpsDialog(false);
6459
+ toast2(
6460
+ /* @__PURE__ */ React.createElement("div", { dir: "rtl", style: { textAlign: "right" } }, /* @__PURE__ */ React.createElement(Typography19, { variant: "body2", sx: { mb: 1 } }, "\u0644\u0637\u0641\u0627\u064B \u0645\u0631\u0627\u062D\u0644 \u0632\u06CC\u0631 \u0631\u0627 \u0627\u0646\u062C\u0627\u0645 \u062F\u0647\u06CC\u062F:"), /* @__PURE__ */ React.createElement(Typography19, { variant: "caption", component: "div" }, "\u06F1. \u062A\u0646\u0638\u06CC\u0645\u0627\u062A \u062F\u0633\u062A\u06AF\u0627\u0647"), /* @__PURE__ */ React.createElement(Typography19, { variant: "caption", component: "div" }, "\u06F2. \u0645\u0648\u0642\u0639\u06CC\u062A \u0645\u06A9\u0627\u0646\u06CC"), /* @__PURE__ */ React.createElement(Typography19, { variant: "caption", component: "div" }, "\u06F3. \u0641\u0639\u0627\u0644 \u06A9\u0631\u062F\u0646 GPS")),
6461
+ { duration: 8e3 }
6462
+ );
6463
+ };
6464
+ return /* @__PURE__ */ React.createElement(Box27, { sx: { width: "100%" } }, /* @__PURE__ */ React.createElement(Toaster, { position: "top-center" }), /* @__PURE__ */ React.createElement(Dialog10, { open: showGpsDialog, onClose: handleGpsDialogClose }, /* @__PURE__ */ React.createElement(DialogTitle8, null, "\u0645\u0634\u06A9\u0644 \u062F\u0631 \u062F\u0631\u06CC\u0627\u0641\u062A \u0645\u0648\u0642\u0639\u06CC\u062A \u0645\u06A9\u0627\u0646\u06CC"), /* @__PURE__ */ React.createElement(DialogContent8, null, gpsErrorType === "permission" ? /* @__PURE__ */ React.createElement(Typography19, null, "\u062F\u0633\u062A\u0631\u0633\u06CC \u0628\u0647 \u0645\u0648\u0642\u0639\u06CC\u062A \u0645\u06A9\u0627\u0646\u06CC \u0631\u062F \u0634\u062F\u0647 \u0627\u0633\u062A.", /* @__PURE__ */ React.createElement("br", null), "\u06F1. \u0631\u0648\u06CC \u0622\u06CC\u06A9\u0648\u0646 \u0642\u0641\u0644 \u062F\u0631 \u0646\u0648\u0627\u0631 \u0622\u062F\u0631\u0633 \u06A9\u0644\u06CC\u06A9 \u06A9\u0646\u06CC\u062F", /* @__PURE__ */ React.createElement("br", null), "\u06F2. \u0645\u0648\u0642\u0639\u06CC\u062A \u0645\u06A9\u0627\u0646\u06CC \u2192 \u0627\u062C\u0627\u0632\u0647 \u062F\u0647\u06CC\u062F") : gpsErrorType === "gps_off" ? /* @__PURE__ */ React.createElement(Typography19, null, "GPS \u062E\u0627\u0645\u0648\u0634 \u0627\u0633\u062A. \u0644\u0637\u0641\u0627\u064B \u0641\u0639\u0627\u0644 \u06A9\u0646\u06CC\u062F.") : /* @__PURE__ */ React.createElement(Typography19, null, "\u062E\u0637\u0627\u06CC\u06CC \u0631\u062E \u062F\u0627\u062F\u0647 \u0627\u0633\u062A. \u062F\u0648\u0628\u0627\u0631\u0647 \u062A\u0644\u0627\u0634 \u06A9\u0646\u06CC\u062F.")), /* @__PURE__ */ React.createElement(DialogActions5, { sx: { pb: 2, pr: 2 } }, /* @__PURE__ */ React.createElement(Button15, { onClick: handleGpsDialogClose }, "\u0627\u0646\u0635\u0631\u0627\u0641"), gpsErrorType === "permission" ? /* @__PURE__ */ React.createElement(
6465
+ Button15,
6466
+ {
6467
+ onClick: handleRetryLocation,
6468
+ variant: "contained",
6469
+ color: "primary"
6470
+ },
6471
+ "\u062A\u0644\u0627\u0634 \u0645\u062C\u062F\u062F"
6472
+ ) : /* @__PURE__ */ React.createElement(
6473
+ Button15,
6474
+ {
6475
+ onClick: handleOpenSettings,
6476
+ variant: "contained",
6477
+ color: "primary"
6478
+ },
6479
+ "\u0631\u0627\u0647\u0646\u0645\u0627"
6480
+ ))), /* @__PURE__ */ React.createElement(Box27, { sx: { position: "relative", height: "370px", width: "100%" } }, /* @__PURE__ */ React.createElement(
6481
+ Paper2,
6482
+ {
6483
+ sx: {
6484
+ height: "100%",
6485
+ position: "relative",
6486
+ overflow: "hidden",
6487
+ borderRadius: 2,
6488
+ border: error ? 2 : 0,
6489
+ borderColor: error ? "error.main" : "transparent"
6490
+ }
6491
+ },
6492
+ (isMapLoading || isGeocoding || isGeolocating || isLoading) && /* @__PURE__ */ React.createElement(
6493
+ Box27,
6494
+ {
6495
+ sx: {
6496
+ position: "absolute",
6497
+ inset: 0,
6498
+ display: "flex",
6499
+ alignItems: "center",
6500
+ justifyContent: "center",
6501
+ backgroundColor: isDarkMode ? "rgba(18,18,18,0.9)" : "rgba(255,255,255,0.8)",
6502
+ zIndex: 1e3,
6503
+ flexDirection: "column",
6504
+ gap: 1
6505
+ }
6506
+ },
6507
+ /* @__PURE__ */ React.createElement(CircularProgress11, null),
6508
+ /* @__PURE__ */ React.createElement(Typography19, null, isMapLoading ? "\u062F\u0631 \u062D\u0627\u0644 \u0628\u0627\u0631\u06AF\u0630\u0627\u0631\u06CC \u0646\u0642\u0634\u0647..." : isLoading ? "\u062F\u0631 \u062D\u0627\u0644 \u0628\u0627\u0631\u06AF\u0630\u0627\u0631\u06CC \u0627\u0637\u0644\u0627\u0639\u0627\u062A \u0645\u0648\u0642\u0639\u06CC\u062A..." : isGeocoding ? "\u062F\u0631 \u062D\u0627\u0644 \u062F\u0631\u06CC\u0627\u0641\u062A \u0622\u062F\u0631\u0633..." : "\u062F\u0631 \u062D\u0627\u0644 \u062F\u0631\u06CC\u0627\u0641\u062A \u0645\u0648\u0642\u0639\u06CC\u062A \u0641\u0639\u0644\u06CC...")
6509
+ ),
6510
+ /* @__PURE__ */ React.createElement("div", { ref: mapRef, style: { height: "100%", width: "100%" } }),
6511
+ /* @__PURE__ */ React.createElement(
6512
+ Box27,
6513
+ {
6514
+ sx: {
6515
+ position: "absolute",
6516
+ bottom: 16,
6517
+ right: 16,
6518
+ display: "flex",
6519
+ flexDirection: "column",
6520
+ gap: 1,
6521
+ zIndex: 1e3
6522
+ }
6523
+ },
6524
+ /* @__PURE__ */ React.createElement(
6525
+ IconButton14,
6526
+ {
6527
+ onClick: handleCurrentLocation,
6528
+ disabled: isGeolocating,
6529
+ sx: {
6530
+ backgroundColor: isDarkMode ? "#1e1e1e" : "white",
6531
+ boxShadow: 2,
6532
+ "&:hover": {
6533
+ backgroundColor: isDarkMode ? "#2a2a2a" : "#f5f5f5"
6534
+ }
6535
+ },
6536
+ title: "\u0645\u0648\u0642\u0639\u06CC\u062A \u0641\u0639\u0644\u06CC"
6537
+ },
6538
+ /* @__PURE__ */ React.createElement(MdMyLocation, null)
6539
+ ),
6540
+ hasExistingLocation && /* @__PURE__ */ React.createElement(
6541
+ IconButton14,
6542
+ {
6543
+ onClick: handleClearLocation,
6544
+ sx: {
6545
+ backgroundColor: isDarkMode ? "#1e1e1e" : "white",
6546
+ color: theme3.palette.error.main,
6547
+ boxShadow: 2,
6548
+ "&:hover": {
6549
+ backgroundColor: isDarkMode ? "#2a2a2a" : "#f5f5f5"
6550
+ }
6551
+ },
6552
+ title: "\u067E\u0627\u06A9 \u06A9\u0631\u062F\u0646 \u0645\u0648\u0642\u0639\u06CC\u062A"
6553
+ },
6554
+ /* @__PURE__ */ React.createElement(MdOutlineClear2, null)
6555
+ )
6556
+ )
6557
+ )), /* @__PURE__ */ React.createElement(
6558
+ Box27,
6559
+ {
6560
+ sx: {
6561
+ mt: 1,
6562
+ p: 1.5,
6563
+ backgroundColor: isDarkMode ? "#1e1e1e" : "#f5f5f5",
6564
+ borderRadius: 1,
6565
+ display: "flex",
6566
+ justifyContent: "space-between",
6567
+ alignItems: "center"
6568
+ }
6569
+ },
6570
+ /* @__PURE__ */ React.createElement(Typography19, { variant: "body2", sx: { fontFamily: "monospace" } }, /* @__PURE__ */ React.createElement("strong", null, "\u0639\u0631\u0636 \u062C\u063A\u0631\u0627\u0641\u06CC\u0627\u06CC\u06CC:"), " ", typeof latitude === "number" ? latitude.toFixed(6) : "\u2014"),
6571
+ /* @__PURE__ */ React.createElement(Typography19, { variant: "body2", sx: { fontFamily: "monospace" } }, /* @__PURE__ */ React.createElement("strong", null, "\u0637\u0648\u0644 \u062C\u063A\u0631\u0627\u0641\u06CC\u0627\u06CC\u06CC:"), " ", typeof longitude === "number" ? longitude.toFixed(6) : "\u2014")
6572
+ ), error && helperText && /* @__PURE__ */ React.createElement(
6573
+ Typography19,
6574
+ {
6575
+ variant: "caption",
6576
+ sx: {
6577
+ color: "error.main",
6578
+ mt: 0.5,
6579
+ display: "block",
6580
+ px: 1.5
6581
+ }
6582
+ },
6583
+ helperText
6584
+ ));
6585
+ }
6166
6586
  export {
6167
6587
  Page as Accordion,
6168
6588
  AdvancedSearchButton,
@@ -6186,6 +6606,7 @@ export {
6186
6606
  HorizontalStepper,
6187
6607
  imgViewer as ImgViewer,
6188
6608
  LicensePlate,
6609
+ MapLocationPicker,
6189
6610
  mobile_date_time_picker_default as MobileDateTimePicker,
6190
6611
  ConfirmationDialog2 as Modal,
6191
6612
  selector_default as NestedSelectort,