react-image-marker-viewer 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,102 @@
1
+ # React Image Marker Viewer
2
+
3
+ A powerful, responsive, and customizable image annotation library for React.
4
+ Add markers to images, edit details, upload photos, and manage state with ease.
5
+
6
+ ## Features
7
+
8
+ - 🎯 **Responsive Markers**: Markers stay in position (percentage-based) regardless of image resizing.
9
+ - 🖼️ **Image Support**: Works with any image URL or base64 string.
10
+ - 📝 **Rich Details**: Add titles, notes, and colors to markers.
11
+ - 📸 **Photo Attachments**: Upload up to 3 photos per marker (stored as Base64).
12
+ - 🔍 **Lightbox**: Built-in full-screen lightbox with zoom and pan support.
13
+ - 🖱️ **Drag & Drop**: Drag markers to reposition them.
14
+ - 🗑️ **Long Press Delete**: Hold a marker for 500ms to delete it.
15
+ - 🌍 **Localization**: Auto-detects English (`en`), Turkish (`tr`), and Spanish (`es`).
16
+ - ✅ **Validation**: Enforces required fields (e.g., Title) and prevents empty saves.
17
+ - 🎨 **Customizable**: Built with Tailwind CSS logic (classes are scoped).
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ npm install react-image-marker-viewer
23
+ # or
24
+ yarn add react-image-marker-viewer
25
+ ```
26
+
27
+ ## Basic Usage
28
+
29
+ ```jsx
30
+ import { ImageArea, useMarkerState } from 'react-image-marker-viewer';
31
+ import 'react-image-marker-viewer/dist/style.css'; // Don't forget CSS!
32
+
33
+ function App() {
34
+ const { markers, addMarker, updateMarker, removeMarker } = useMarkerState();
35
+
36
+ return (
37
+ <ImageArea
38
+ src="https://example.com/image.jpg"
39
+ markers={markers}
40
+ onAddMarker={addMarker}
41
+ onUpdateMarker={updateMarker}
42
+ onDeleteMarker={removeMarker}
43
+ />
44
+ );
45
+ }
46
+ ```
47
+
48
+ ## Components
49
+
50
+ ### `ImageArea`
51
+
52
+ The main component that renders the image and markers.
53
+
54
+ | Prop | Type | Description |
55
+ |------|------|-------------|
56
+ | `src` | `string` | URL or Base64 string of the image. |
57
+ | `markers` | `Marker[]` | Array of marker objects. |
58
+ | `onAddMarker` | `(marker: Marker) => void` | Callback when a new marker is saved. |
59
+ | `onUpdateMarker` | `(id: string, updates: Partial<Marker>) => void` | Callback when a marker is updated. |
60
+ | `onDeleteMarker` | `(id: string) => void` | Callback when a marker is deleted. |
61
+
62
+ ### `useMarkerState` Hook
63
+
64
+ A helper hook to manage marker state easily.
65
+
66
+ ```typescript
67
+ const {
68
+ markers, // Current array of markers
69
+ addMarker, // Function to add a marker
70
+ updateMarker, // Function to update a marker
71
+ removeMarker, // Function to delete a marker
72
+ setAllMarkers // Function to replace all markers (e.g. loading from DB)
73
+ } = useMarkerState();
74
+ ```
75
+
76
+ ## Types
77
+
78
+ ### `Marker`
79
+
80
+ ```typescript
81
+ interface Marker {
82
+ id: string;
83
+ x: number; // Percentage (0-100)
84
+ y: number; // Percentage (0-100)
85
+ color: number; // Integer color value
86
+ markerTitle: string;
87
+ note: string;
88
+ photos: string[]; // Base64 strings
89
+ photosName: string[]; // File names
90
+ }
91
+ ```
92
+
93
+ ## Localization
94
+
95
+ The library automatically detects the browser language. Currently supported:
96
+ - English (`en`) - Default
97
+ - Turkish (`tr`)
98
+ - Spanish (`es`)
99
+
100
+ ## License
101
+
102
+ MIT
@@ -0,0 +1 @@
1
+ *,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.\!container{width:100%!important}.container{width:100%}@media(min-width:640px){.\!container{max-width:640px!important}.container{max-width:640px}}@media(min-width:768px){.\!container{max-width:768px!important}.container{max-width:768px}}@media(min-width:1024px){.\!container{max-width:1024px!important}.container{max-width:1024px}}@media(min-width:1280px){.\!container{max-width:1280px!important}.container{max-width:1280px}}@media(min-width:1536px){.\!container{max-width:1536px!important}.container{max-width:1536px}}.pointer-events-none{pointer-events:none}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{top:0;right:0;bottom:0;left:0}.-right-1{right:-.25rem}.-top-1{top:-.25rem}.bottom-4{bottom:1rem}.left-0{left:0}.left-4{left:1rem}.right-0{right:0}.right-4{right:1rem}.top-4{top:1rem}.z-40{z-index:40}.z-50{z-index:50}.z-\[60\]{z-index:60}.mx-auto{margin-left:auto;margin-right:auto}.mb-4{margin-bottom:1rem}.mb-8{margin-bottom:2rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-4{margin-top:1rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.hidden{display:none}.h-10{height:2.5rem}.h-16{height:4rem}.h-2{height:.5rem}.h-4{height:1rem}.h-6{height:1.5rem}.h-8{height:2rem}.h-auto{height:auto}.h-full{height:100%}.h-screen{height:100vh}.max-h-\[90vh\]{max-height:90vh}.min-h-screen{min-height:100vh}.w-10{width:2.5rem}.w-16{width:4rem}.w-2{width:.5rem}.w-4{width:1rem}.w-6{width:1.5rem}.w-72{width:18rem}.w-8{width:2rem}.w-full{width:100%}.max-w-5xl{max-width:64rem}.max-w-full{max-width:100%}.-translate-x-1\/2{--tw-translate-x: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-y-1\/2{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-125{--tw-scale-x: 1.25;--tw-scale-y: 1.25;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-crosshair{cursor:crosshair}.cursor-pointer{cursor:pointer}.touch-none{touch-action:none}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-2{gap:.5rem}.gap-4{gap:1rem}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-2{border-width:2px}.border-dashed{border-style:dashed}.border-gray-100{--tw-border-opacity: 1;border-color:rgb(243 244 246 / var(--tw-border-opacity, 1))}.border-gray-200{--tw-border-opacity: 1;border-color:rgb(229 231 235 / var(--tw-border-opacity, 1))}.border-gray-300{--tw-border-opacity: 1;border-color:rgb(209 213 219 / var(--tw-border-opacity, 1))}.border-red-500{--tw-border-opacity: 1;border-color:rgb(239 68 68 / var(--tw-border-opacity, 1))}.border-white{--tw-border-opacity: 1;border-color:rgb(255 255 255 / var(--tw-border-opacity, 1))}.bg-black{--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1))}.bg-black\/50{background-color:#00000080}.bg-blue-500{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity, 1))}.bg-blue-600{--tw-bg-opacity: 1;background-color:rgb(37 99 235 / var(--tw-bg-opacity, 1))}.bg-gray-100{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity, 1))}.bg-gray-900{--tw-bg-opacity: 1;background-color:rgb(17 24 39 / var(--tw-bg-opacity, 1))}.bg-red-500{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity, 1))}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.bg-opacity-90{--tw-bg-opacity: .9}.object-contain{-o-object-fit:contain;object-fit:contain}.object-cover{-o-object-fit:cover;object-fit:cover}.p-0{padding:0}.p-0\.5{padding:.125rem}.p-2{padding:.5rem}.p-4{padding:1rem}.p-8{padding:2rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.text-center{text-align:center}.font-sans{font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji"}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-4xl{font-size:2.25rem;line-height:2.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.text-gray-600{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity, 1))}.text-gray-700{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity, 1))}.text-gray-900{--tw-text-opacity: 1;color:rgb(17 24 39 / var(--tw-text-opacity, 1))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.text-red-600{--tw-text-opacity: 1;color:rgb(220 38 38 / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.opacity-0{opacity:0}.opacity-50{opacity:.5}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.ring-2{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-blue-500{--tw-ring-opacity: 1;--tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity, 1))}.ring-offset-1{--tw-ring-offset-width: 1px}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.hover\:scale-110:hover{--tw-scale-x: 1.1;--tw-scale-y: 1.1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover\:border-blue-500:hover{--tw-border-opacity: 1;border-color:rgb(59 130 246 / var(--tw-border-opacity, 1))}.hover\:bg-blue-50:hover{--tw-bg-opacity: 1;background-color:rgb(239 246 255 / var(--tw-bg-opacity, 1))}.hover\:bg-blue-600:hover{--tw-bg-opacity: 1;background-color:rgb(37 99 235 / var(--tw-bg-opacity, 1))}.hover\:bg-blue-700:hover{--tw-bg-opacity: 1;background-color:rgb(29 78 216 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-50:hover{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity, 1))}.hover\:bg-red-50:hover{--tw-bg-opacity: 1;background-color:rgb(254 242 242 / var(--tw-bg-opacity, 1))}.hover\:bg-white\/10:hover{background-color:#ffffff1a}.hover\:text-gray-300:hover{--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity, 1))}.hover\:text-gray-600:hover{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity, 1))}.focus\:border-blue-500:focus{--tw-border-opacity: 1;border-color:rgb(59 130 246 / var(--tw-border-opacity, 1))}.focus\:border-red-500:focus{--tw-border-opacity: 1;border-color:rgb(239 68 68 / var(--tw-border-opacity, 1))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-1:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-2:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-blue-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity, 1))}.focus\:ring-red-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(239 68 68 / var(--tw-ring-opacity, 1))}.focus\:ring-offset-1:focus{--tw-ring-offset-width: 1px}.group:hover .group-hover\:opacity-100{opacity:1}@media(min-width:640px){.sm\:text-sm{font-size:.875rem;line-height:1.25rem}}
@@ -0,0 +1 @@
1
+ export {}
@@ -0,0 +1,811 @@
1
+ import de, { useMemo as fe, useState as N, useRef as T, useCallback as M, useEffect as q } from "react";
2
+ var Y = { exports: {} }, I = {};
3
+ /**
4
+ * @license React
5
+ * react-jsx-runtime.production.js
6
+ *
7
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
8
+ *
9
+ * This source code is licensed under the MIT license found in the
10
+ * LICENSE file in the root directory of this source tree.
11
+ */
12
+ var ee;
13
+ function me() {
14
+ if (ee) return I;
15
+ ee = 1;
16
+ var t = Symbol.for("react.transitional.element"), u = Symbol.for("react.fragment");
17
+ function i(o, n, s) {
18
+ var x = null;
19
+ if (s !== void 0 && (x = "" + s), n.key !== void 0 && (x = "" + n.key), "key" in n) {
20
+ s = {};
21
+ for (var c in n)
22
+ c !== "key" && (s[c] = n[c]);
23
+ } else s = n;
24
+ return n = s.ref, {
25
+ $$typeof: t,
26
+ type: o,
27
+ key: x,
28
+ ref: n !== void 0 ? n : null,
29
+ props: s
30
+ };
31
+ }
32
+ return I.Fragment = u, I.jsx = i, I.jsxs = i, I;
33
+ }
34
+ var F = {};
35
+ /**
36
+ * @license React
37
+ * react-jsx-runtime.development.js
38
+ *
39
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
40
+ *
41
+ * This source code is licensed under the MIT license found in the
42
+ * LICENSE file in the root directory of this source tree.
43
+ */
44
+ var te;
45
+ function he() {
46
+ return te || (te = 1, process.env.NODE_ENV !== "production" && (function() {
47
+ function t(e) {
48
+ if (e == null) return null;
49
+ if (typeof e == "function")
50
+ return e.$$typeof === ie ? null : e.displayName || e.name || null;
51
+ if (typeof e == "string") return e;
52
+ switch (e) {
53
+ case C:
54
+ return "Fragment";
55
+ case A:
56
+ return "Profiler";
57
+ case p:
58
+ return "StrictMode";
59
+ case w:
60
+ return "Suspense";
61
+ case P:
62
+ return "SuspenseList";
63
+ case le:
64
+ return "Activity";
65
+ }
66
+ if (typeof e == "object")
67
+ switch (typeof e.tag == "number" && console.error(
68
+ "Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."
69
+ ), e.$$typeof) {
70
+ case E:
71
+ return "Portal";
72
+ case R:
73
+ return e.displayName || "Context";
74
+ case l:
75
+ return (e._context.displayName || "Context") + ".Consumer";
76
+ case _:
77
+ var a = e.render;
78
+ return e = e.displayName, e || (e = a.displayName || a.name || "", e = e !== "" ? "ForwardRef(" + e + ")" : "ForwardRef"), e;
79
+ case O:
80
+ return a = e.displayName || null, a !== null ? a : t(e.type) || "Memo";
81
+ case L:
82
+ a = e._payload, e = e._init;
83
+ try {
84
+ return t(e(a));
85
+ } catch {
86
+ }
87
+ }
88
+ return null;
89
+ }
90
+ function u(e) {
91
+ return "" + e;
92
+ }
93
+ function i(e) {
94
+ try {
95
+ u(e);
96
+ var a = !1;
97
+ } catch {
98
+ a = !0;
99
+ }
100
+ if (a) {
101
+ a = console;
102
+ var b = a.error, y = typeof Symbol == "function" && Symbol.toStringTag && e[Symbol.toStringTag] || e.constructor.name || "Object";
103
+ return b.call(
104
+ a,
105
+ "The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",
106
+ y
107
+ ), u(e);
108
+ }
109
+ }
110
+ function o(e) {
111
+ if (e === C) return "<>";
112
+ if (typeof e == "object" && e !== null && e.$$typeof === L)
113
+ return "<...>";
114
+ try {
115
+ var a = t(e);
116
+ return a ? "<" + a + ">" : "<...>";
117
+ } catch {
118
+ return "<...>";
119
+ }
120
+ }
121
+ function n() {
122
+ var e = $.A;
123
+ return e === null ? null : e.getOwner();
124
+ }
125
+ function s() {
126
+ return Error("react-stack-top-frame");
127
+ }
128
+ function x(e) {
129
+ if (G.call(e, "key")) {
130
+ var a = Object.getOwnPropertyDescriptor(e, "key").get;
131
+ if (a && a.isReactWarning) return !1;
132
+ }
133
+ return e.key !== void 0;
134
+ }
135
+ function c(e, a) {
136
+ function b() {
137
+ J || (J = !0, console.error(
138
+ "%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",
139
+ a
140
+ ));
141
+ }
142
+ b.isReactWarning = !0, Object.defineProperty(e, "key", {
143
+ get: b,
144
+ configurable: !0
145
+ });
146
+ }
147
+ function d() {
148
+ var e = t(this.type);
149
+ return H[e] || (H[e] = !0, console.error(
150
+ "Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release."
151
+ )), e = this.props.ref, e !== void 0 ? e : null;
152
+ }
153
+ function f(e, a, b, y, D, U) {
154
+ var k = b.ref;
155
+ return e = {
156
+ $$typeof: g,
157
+ type: e,
158
+ key: a,
159
+ props: b,
160
+ _owner: y
161
+ }, (k !== void 0 ? k : null) !== null ? Object.defineProperty(e, "ref", {
162
+ enumerable: !1,
163
+ get: d
164
+ }) : Object.defineProperty(e, "ref", { enumerable: !1, value: null }), e._store = {}, Object.defineProperty(e._store, "validated", {
165
+ configurable: !1,
166
+ enumerable: !1,
167
+ writable: !0,
168
+ value: 0
169
+ }), Object.defineProperty(e, "_debugInfo", {
170
+ configurable: !1,
171
+ enumerable: !1,
172
+ writable: !0,
173
+ value: null
174
+ }), Object.defineProperty(e, "_debugStack", {
175
+ configurable: !1,
176
+ enumerable: !1,
177
+ writable: !0,
178
+ value: D
179
+ }), Object.defineProperty(e, "_debugTask", {
180
+ configurable: !1,
181
+ enumerable: !1,
182
+ writable: !0,
183
+ value: U
184
+ }), Object.freeze && (Object.freeze(e.props), Object.freeze(e)), e;
185
+ }
186
+ function h(e, a, b, y, D, U) {
187
+ var k = a.children;
188
+ if (k !== void 0)
189
+ if (y)
190
+ if (ce(k)) {
191
+ for (y = 0; y < k.length; y++)
192
+ v(k[y]);
193
+ Object.freeze && Object.freeze(k);
194
+ } else
195
+ console.error(
196
+ "React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead."
197
+ );
198
+ else v(k);
199
+ if (G.call(a, "key")) {
200
+ k = t(e);
201
+ var S = Object.keys(a).filter(function(ue) {
202
+ return ue !== "key";
203
+ });
204
+ y = 0 < S.length ? "{key: someKey, " + S.join(": ..., ") + ": ...}" : "{key: someKey}", Q[k + y] || (S = 0 < S.length ? "{" + S.join(": ..., ") + ": ...}" : "{}", console.error(
205
+ `A props object containing a "key" prop is being spread into JSX:
206
+ let props = %s;
207
+ <%s {...props} />
208
+ React keys must be passed directly to JSX without using spread:
209
+ let props = %s;
210
+ <%s key={someKey} {...props} />`,
211
+ y,
212
+ k,
213
+ S,
214
+ k
215
+ ), Q[k + y] = !0);
216
+ }
217
+ if (k = null, b !== void 0 && (i(b), k = "" + b), x(a) && (i(a.key), k = "" + a.key), "key" in a) {
218
+ b = {};
219
+ for (var z in a)
220
+ z !== "key" && (b[z] = a[z]);
221
+ } else b = a;
222
+ return k && c(
223
+ b,
224
+ typeof e == "function" ? e.displayName || e.name || "Unknown" : e
225
+ ), f(
226
+ e,
227
+ k,
228
+ b,
229
+ n(),
230
+ D,
231
+ U
232
+ );
233
+ }
234
+ function v(e) {
235
+ j(e) ? e._store && (e._store.validated = 1) : typeof e == "object" && e !== null && e.$$typeof === L && (e._payload.status === "fulfilled" ? j(e._payload.value) && e._payload.value._store && (e._payload.value._store.validated = 1) : e._store && (e._store.validated = 1));
236
+ }
237
+ function j(e) {
238
+ return typeof e == "object" && e !== null && e.$$typeof === g;
239
+ }
240
+ var m = de, g = Symbol.for("react.transitional.element"), E = Symbol.for("react.portal"), C = Symbol.for("react.fragment"), p = Symbol.for("react.strict_mode"), A = Symbol.for("react.profiler"), l = Symbol.for("react.consumer"), R = Symbol.for("react.context"), _ = Symbol.for("react.forward_ref"), w = Symbol.for("react.suspense"), P = Symbol.for("react.suspense_list"), O = Symbol.for("react.memo"), L = Symbol.for("react.lazy"), le = Symbol.for("react.activity"), ie = Symbol.for("react.client.reference"), $ = m.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, G = Object.prototype.hasOwnProperty, ce = Array.isArray, B = console.createTask ? console.createTask : function() {
241
+ return null;
242
+ };
243
+ m = {
244
+ react_stack_bottom_frame: function(e) {
245
+ return e();
246
+ }
247
+ };
248
+ var J, H = {}, Z = m.react_stack_bottom_frame.bind(
249
+ m,
250
+ s
251
+ )(), K = B(o(s)), Q = {};
252
+ F.Fragment = C, F.jsx = function(e, a, b) {
253
+ var y = 1e4 > $.recentlyCreatedOwnerStacks++;
254
+ return h(
255
+ e,
256
+ a,
257
+ b,
258
+ !1,
259
+ y ? Error("react-stack-top-frame") : Z,
260
+ y ? B(o(e)) : K
261
+ );
262
+ }, F.jsxs = function(e, a, b) {
263
+ var y = 1e4 > $.recentlyCreatedOwnerStacks++;
264
+ return h(
265
+ e,
266
+ a,
267
+ b,
268
+ !0,
269
+ y ? Error("react-stack-top-frame") : Z,
270
+ y ? B(o(e)) : K
271
+ );
272
+ };
273
+ })()), F;
274
+ }
275
+ var re;
276
+ function xe() {
277
+ return re || (re = 1, process.env.NODE_ENV === "production" ? Y.exports = me() : Y.exports = he()), Y.exports;
278
+ }
279
+ var r = xe();
280
+ const ge = (t, u) => {
281
+ const i = u.getBoundingClientRect();
282
+ let o, n;
283
+ "touches" in t ? (o = t.touches[0].clientX, n = t.touches[0].clientY) : (o = t.clientX, n = t.clientY);
284
+ const s = (o - i.left) / i.width * 100, x = (n - i.top) / i.height * 100;
285
+ return {
286
+ x: Math.max(0, Math.min(100, s)),
287
+ y: Math.max(0, Math.min(100, x))
288
+ };
289
+ }, X = (t) => parseInt(t.replace(/^#/, ""), 16), ae = (t) => {
290
+ const u = t.toString(16).toUpperCase();
291
+ return `#${"0".repeat(6 - u.length)}${u}`;
292
+ }, W = {
293
+ en: {
294
+ editMarker: "Edit Marker",
295
+ title: "Title",
296
+ note: "Note",
297
+ photos: "Photos",
298
+ delete: "Delete",
299
+ cancel: "Cancel",
300
+ save: "Save",
301
+ newMarker: "New Marker",
302
+ uploadImage: "Upload Image",
303
+ deleteConfirmation: "Are you sure you want to delete this marker?",
304
+ confirmClear: "Clear existing markers for new image?",
305
+ titlePlaceholder: "Enter a title",
306
+ notePlaceholder: "Enter a note",
307
+ titleRequired: "Title is required"
308
+ },
309
+ tr: {
310
+ editMarker: "İşaretçiyi Düzenle",
311
+ title: "Başlık",
312
+ note: "Not",
313
+ photos: "Fotoğraflar",
314
+ delete: "Sil",
315
+ cancel: "İptal",
316
+ save: "Kaydet",
317
+ newMarker: "Yeni İşaretçi",
318
+ uploadImage: "Görsel Yükle",
319
+ deleteConfirmation: "Bu işaretçiyi silmek istediğinizden emin misiniz?",
320
+ confirmClear: "Yeni görsel için mevcut işaretçileri temizle?",
321
+ titlePlaceholder: "Bir başlık girin",
322
+ notePlaceholder: "Bir not girin",
323
+ titleRequired: "Başlık zorunludur"
324
+ },
325
+ es: {
326
+ editMarker: "Editar Marcador",
327
+ title: "Título",
328
+ note: "Nota",
329
+ photos: "Fotos",
330
+ delete: "Eliminar",
331
+ cancel: "Cancelar",
332
+ save: "Guardar",
333
+ newMarker: "Nuevo Marcador",
334
+ uploadImage: "Subir Imagen",
335
+ deleteConfirmation: "¿Estás seguro de que deseas eliminar este marcador?",
336
+ confirmClear: "¿Borrar marcadores existentes para la nueva imagen?",
337
+ titlePlaceholder: "Ingrese un título",
338
+ notePlaceholder: "Ingrese una nota",
339
+ titleRequired: "El título es obligatorio"
340
+ }
341
+ }, V = () => fe(() => {
342
+ const u = navigator.language.split("-")[0];
343
+ return u === "tr" ? W.tr : u === "es" ? W.es : W.en;
344
+ }, []), ne = ({ marker: t, onClick: u, onUpdate: i, onDelete: o }) => {
345
+ const n = ae(t.color), [s, x] = N(!1), c = T(null), d = V(), f = T(null), h = T(!1), v = T({ x: 0, y: 0 }), j = T(!1), m = (p) => {
346
+ p.stopPropagation(), p.preventDefault(), j.current = !1, h.current = !1, v.current = { x: p.clientX, y: p.clientY }, f.current = window.setTimeout(() => {
347
+ h.current = !0, window.confirm(d.deleteConfirmation) && o(t.id);
348
+ }, 500), x(!0), p.target.setPointerCapture(p.pointerId);
349
+ }, g = (p) => {
350
+ var R;
351
+ if (!s) return;
352
+ Math.sqrt(Math.pow(p.clientX - v.current.x, 2) + Math.pow(p.clientY - v.current.y, 2)) > 5 && (j.current = !0, f.current && (clearTimeout(f.current), f.current = null));
353
+ const l = (R = c.current) == null ? void 0 : R.parentElement;
354
+ if (l) {
355
+ const _ = l.getBoundingClientRect();
356
+ let w = (p.clientX - _.left) / _.width * 100, P = (p.clientY - _.top) / _.height * 100;
357
+ w = Math.max(0, Math.min(100, w)), P = Math.max(0, Math.min(100, P)), i(t.id, { x: w, y: P });
358
+ }
359
+ }, E = (p) => {
360
+ p.stopPropagation(), x(!1), p.target.releasePointerCapture(p.pointerId), f.current && (clearTimeout(f.current), f.current = null);
361
+ }, C = (p) => {
362
+ p.stopPropagation(), !j.current && !h.current && u(t);
363
+ };
364
+ return /* @__PURE__ */ r.jsx(
365
+ "div",
366
+ {
367
+ ref: c,
368
+ onPointerDown: m,
369
+ onPointerMove: g,
370
+ onPointerUp: E,
371
+ onClick: C,
372
+ style: {
373
+ left: `${t.x}%`,
374
+ top: `${t.y}%`,
375
+ backgroundColor: n,
376
+ touchAction: "none"
377
+ },
378
+ className: `absolute h-6 w-6 -translate-x-1/2 -translate-y-1/2 cursor-pointer rounded-full border-2 border-white shadow-md flex items-center justify-center transition-transform ${s ? "scale-125 z-50" : "hover:scale-110"}`,
379
+ children: /* @__PURE__ */ r.jsx("div", { className: "w-2 h-2 bg-white rounded-full opacity-50" })
380
+ }
381
+ );
382
+ }, se = [
383
+ { name: "Red", hex: "#EF4444" },
384
+ { name: "Orange", hex: "#F97316" },
385
+ { name: "Amber", hex: "#F59E0B" },
386
+ { name: "Yellow", hex: "#EAB308" },
387
+ { name: "Lime", hex: "#84CC16" },
388
+ { name: "Green", hex: "#22C55E" },
389
+ { name: "Emerald", hex: "#10B981" },
390
+ { name: "Teal", hex: "#14B8A6" },
391
+ { name: "Cyan", hex: "#06B6D4" },
392
+ { name: "Sky", hex: "#0EA5E9" },
393
+ { name: "Blue", hex: "#3B82F6" },
394
+ { name: "Indigo", hex: "#6366F1" },
395
+ { name: "Violet", hex: "#8B5CF6" },
396
+ { name: "Purple", hex: "#A855F7" },
397
+ { name: "Fuchsia", hex: "#D946EF" },
398
+ { name: "Pink", hex: "#EC4899" },
399
+ { name: "Rose", hex: "#F43F5E" },
400
+ { name: "Slate", hex: "#64748B" },
401
+ { name: "Black", hex: "#000000" },
402
+ { name: "White", hex: "#FFFFFF" }
403
+ ];
404
+ X(se[0].hex);
405
+ const pe = ({ color: t, onChange: u }) => {
406
+ const i = ae(t);
407
+ return /* @__PURE__ */ r.jsxs("div", { className: "space-y-2", children: [
408
+ /* @__PURE__ */ r.jsx("label", { className: "block text-sm font-medium text-gray-700", children: "Color" }),
409
+ /* @__PURE__ */ r.jsxs("div", { className: "flex flex-wrap gap-2", children: [
410
+ se.map((o) => /* @__PURE__ */ r.jsx(
411
+ "button",
412
+ {
413
+ onClick: () => u(X(o.hex)),
414
+ className: `h-6 w-6 rounded-full border transition-transform hover:scale-110 focus:outline-none focus:ring-2 focus:ring-offset-1 ${i === o.hex ? "ring-2 ring-blue-500 ring-offset-1" : "border-gray-200"}`,
415
+ style: { backgroundColor: o.hex },
416
+ title: o.name
417
+ },
418
+ o.hex
419
+ )),
420
+ /* @__PURE__ */ r.jsx(
421
+ "input",
422
+ {
423
+ type: "color",
424
+ value: i,
425
+ onChange: (o) => u(X(o.target.value)),
426
+ className: "h-6 w-6 cursor-pointer rounded-full border border-gray-300 p-0 overflow-hidden",
427
+ title: "Custom Color"
428
+ }
429
+ )
430
+ ] })
431
+ ] });
432
+ }, be = (t) => new Promise((u, i) => {
433
+ const o = new FileReader();
434
+ o.readAsDataURL(t), o.onload = () => u(o.result), o.onerror = (n) => i(n);
435
+ }), ve = ({ photos: t, photosNames: u, onUpload: i, onRemove: o, onPhotoClick: n }) => {
436
+ const s = V(), x = async (c) => {
437
+ if (c.target.files) {
438
+ const d = Array.from(c.target.files), f = await Promise.all(d.map(be)), h = d.map((g) => g.name), v = 3 - t.length, j = f.slice(0, v), m = h.slice(0, v);
439
+ i([...t, ...j], [...u, ...m]);
440
+ }
441
+ };
442
+ return /* @__PURE__ */ r.jsxs("div", { className: "space-y-2", children: [
443
+ /* @__PURE__ */ r.jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ r.jsxs("label", { className: "text-sm font-medium text-gray-700", children: [
444
+ s.photos,
445
+ " (",
446
+ t.length,
447
+ "/3)"
448
+ ] }) }),
449
+ /* @__PURE__ */ r.jsxs("div", { className: "flex flex-wrap gap-2", children: [
450
+ t.map((c, d) => /* @__PURE__ */ r.jsxs("div", { className: "relative h-16 w-16 group", children: [
451
+ /* @__PURE__ */ r.jsx(
452
+ "img",
453
+ {
454
+ src: c,
455
+ alt: `Upload ${d}`,
456
+ className: "h-full w-full object-cover rounded border cursor-pointer",
457
+ onClick: () => n && n(d)
458
+ }
459
+ ),
460
+ /* @__PURE__ */ r.jsx(
461
+ "button",
462
+ {
463
+ onClick: () => o(d),
464
+ className: "absolute -top-1 -right-1 bg-red-500 text-white rounded-full p-0.5 w-4 h-4 flex items-center justify-center text-xs opacity-0 group-hover:opacity-100 transition-opacity",
465
+ children: "×"
466
+ }
467
+ )
468
+ ] }, d)),
469
+ t.length < 3 && /* @__PURE__ */ r.jsxs("label", { className: "flex h-16 w-16 cursor-pointer items-center justify-center rounded border-2 border-dashed border-gray-300 hover:border-blue-500 hover:bg-blue-50 transition-colors", children: [
470
+ /* @__PURE__ */ r.jsx("span", { className: "text-xl text-gray-400", children: "+" }),
471
+ /* @__PURE__ */ r.jsx("input", { type: "file", accept: "image/*", multiple: !0, onChange: x, className: "hidden" })
472
+ ] })
473
+ ] })
474
+ ] });
475
+ }, we = ({ photos: t, initialIndex: u, onClose: i }) => {
476
+ const [o, n] = N(u), [s, x] = N(1), [c, d] = N({ x: 0, y: 0 }), [f, h] = N(!1), v = T({ x: 0, y: 0 }), j = T(null), m = M(() => {
477
+ x(1), d({ x: 0, y: 0 });
478
+ }, []), g = M(() => {
479
+ n((l) => (l + 1) % t.length), m();
480
+ }, [t.length, m]), E = M(() => {
481
+ n((l) => (l - 1 + t.length) % t.length), m();
482
+ }, [t.length, m]);
483
+ q(() => {
484
+ const l = (R) => {
485
+ R.key === "Escape" && i(), R.key === "ArrowRight" && g(), R.key === "ArrowLeft" && E();
486
+ };
487
+ return window.addEventListener("keydown", l), () => window.removeEventListener("keydown", l);
488
+ }, [i, g, E]), q(() => {
489
+ const l = j.current;
490
+ if (!l) return;
491
+ const R = (_) => {
492
+ _.preventDefault();
493
+ const w = -_.deltaY * 0.01, P = Math.min(Math.max(1, s + w), 5);
494
+ x(P), P === 1 && d({ x: 0, y: 0 });
495
+ };
496
+ return l.addEventListener("wheel", R, { passive: !1 }), () => l.removeEventListener("wheel", R);
497
+ }, [s]);
498
+ const C = (l) => {
499
+ s > 1 && (l.preventDefault(), h(!0), v.current = { x: l.clientX - c.x, y: l.clientY - c.y }, l.target.setPointerCapture(l.pointerId));
500
+ }, p = (l) => {
501
+ f && s > 1 && (l.preventDefault(), d({
502
+ x: l.clientX - v.current.x,
503
+ y: l.clientY - v.current.y
504
+ }));
505
+ }, A = (l) => {
506
+ h(!1), l.target.releasePointerCapture(l.pointerId);
507
+ };
508
+ return t.length ? /* @__PURE__ */ r.jsxs(
509
+ "div",
510
+ {
511
+ className: "fixed inset-0 z-[60] flex items-center justify-center bg-black bg-opacity-90 overflow-hidden",
512
+ onClick: (l) => {
513
+ l.stopPropagation(), i();
514
+ },
515
+ children: [
516
+ /* @__PURE__ */ r.jsx(
517
+ "button",
518
+ {
519
+ onClick: i,
520
+ className: "absolute top-4 right-4 text-white hover:text-gray-300 z-50 p-2",
521
+ children: /* @__PURE__ */ r.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-8 w-8", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ r.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) })
522
+ }
523
+ ),
524
+ /* @__PURE__ */ r.jsxs(
525
+ "div",
526
+ {
527
+ ref: j,
528
+ className: "relative flex items-center justify-center w-full h-full p-4",
529
+ onClick: (l) => l.stopPropagation(),
530
+ children: [
531
+ t.length > 1 && /* @__PURE__ */ r.jsx(
532
+ "button",
533
+ {
534
+ onClick: E,
535
+ className: "absolute left-4 z-40 p-2 text-white hover:bg-white/10 rounded-full transition-colors",
536
+ children: /* @__PURE__ */ r.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-10 w-10", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ r.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M15 19l-7-7 7-7" }) })
537
+ }
538
+ ),
539
+ /* @__PURE__ */ r.jsx("div", { className: "relative overflow-hidden flex items-center justify-center w-full h-full", children: /* @__PURE__ */ r.jsx(
540
+ "img",
541
+ {
542
+ src: t[o],
543
+ alt: `View ${o + 1}`,
544
+ onPointerDown: C,
545
+ onPointerMove: p,
546
+ onPointerUp: A,
547
+ style: {
548
+ transform: `scale(${s}) translate(${c.x / s}px, ${c.y / s}px)`,
549
+ transition: f ? "none" : "transform 0.1s ease-out",
550
+ cursor: s > 1 ? f ? "grabbing" : "grab" : "default",
551
+ maxHeight: "100%",
552
+ maxWidth: "100%"
553
+ },
554
+ className: "object-contain select-none touch-none"
555
+ }
556
+ ) }),
557
+ t.length > 1 && /* @__PURE__ */ r.jsx(
558
+ "button",
559
+ {
560
+ onClick: g,
561
+ className: "absolute right-4 z-40 p-2 text-white hover:bg-white/10 rounded-full transition-colors",
562
+ children: /* @__PURE__ */ r.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-10 w-10", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ r.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5l7 7-7 7" }) })
563
+ }
564
+ ),
565
+ /* @__PURE__ */ r.jsxs("div", { className: "absolute bottom-4 left-0 right-0 text-center text-white text-sm bg-black/50 py-1 pointer-events-none", children: [
566
+ o + 1,
567
+ " / ",
568
+ t.length,
569
+ " ",
570
+ s > 1 && `(${Math.round(s * 100)}%)`
571
+ ] })
572
+ ]
573
+ }
574
+ )
575
+ ]
576
+ }
577
+ ) : null;
578
+ }, oe = ({ marker: t, onSave: u, onDelete: i, onClose: o }) => {
579
+ const n = V(), [s, x] = N(t.markerTitle), [c, d] = N(t.note), [f, h] = N(t.color), [v, j] = N(t.photos), [m, g] = N(t.photosName || []), [E, C] = N(!1), [p, A] = N(0), [l, R] = N(!1);
580
+ q(() => {
581
+ x(t.markerTitle), d(t.note), h(t.color), j(t.photos), g(t.photosName || []), R(!1);
582
+ }, [t]);
583
+ const _ = () => {
584
+ if (!s.trim()) {
585
+ R(!0);
586
+ return;
587
+ }
588
+ u(t.id, { markerTitle: s, note: c, color: f, photos: v, photosName: m }), o();
589
+ };
590
+ return /* @__PURE__ */ r.jsxs(r.Fragment, { children: [
591
+ /* @__PURE__ */ r.jsx("div", { className: "fixed inset-0 z-40 flex items-center justify-center bg-black/50", onClick: (w) => w.stopPropagation(), children: /* @__PURE__ */ r.jsxs("div", { className: "w-72 max-h-[90vh] overflow-y-auto rounded-lg bg-white p-4 shadow-xl border border-gray-200 relative z-50", children: [
592
+ /* @__PURE__ */ r.jsxs("div", { className: "mb-4 flex items-center justify-between", children: [
593
+ /* @__PURE__ */ r.jsx("h3", { className: "text-lg font-semibold text-gray-900", children: n.editMarker }),
594
+ /* @__PURE__ */ r.jsx("button", { onClick: o, className: "text-gray-400 hover:text-gray-600", children: "✕" })
595
+ ] }),
596
+ /* @__PURE__ */ r.jsxs("div", { className: "space-y-4", children: [
597
+ /* @__PURE__ */ r.jsxs("div", { children: [
598
+ /* @__PURE__ */ r.jsxs("label", { className: "block text-sm font-medium text-gray-700", children: [
599
+ n.title,
600
+ " ",
601
+ /* @__PURE__ */ r.jsx("span", { className: "text-red-500", children: "*" })
602
+ ] }),
603
+ /* @__PURE__ */ r.jsx(
604
+ "input",
605
+ {
606
+ type: "text",
607
+ value: s,
608
+ placeholder: n.titlePlaceholder,
609
+ onChange: (w) => {
610
+ x(w.target.value), l && R(!1);
611
+ },
612
+ className: `mt-1 block w-full rounded-md border px-3 py-2 shadow-sm focus:outline-none focus:ring-1 sm:text-sm ${l ? "border-red-500 focus:border-red-500 focus:ring-red-500" : "border-gray-300 focus:border-blue-500 focus:ring-blue-500"}`
613
+ }
614
+ ),
615
+ l && /* @__PURE__ */ r.jsx("p", { className: "mt-1 text-xs text-red-500", children: n.titleRequired })
616
+ ] }),
617
+ /* @__PURE__ */ r.jsxs("div", { children: [
618
+ /* @__PURE__ */ r.jsx("label", { className: "block text-sm font-medium text-gray-700", children: n.note }),
619
+ /* @__PURE__ */ r.jsx(
620
+ "textarea",
621
+ {
622
+ value: c,
623
+ placeholder: n.notePlaceholder,
624
+ onChange: (w) => d(w.target.value),
625
+ rows: 3,
626
+ className: "mt-1 block w-full rounded-md border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500 sm:text-sm"
627
+ }
628
+ )
629
+ ] }),
630
+ /* @__PURE__ */ r.jsx(pe, { color: f, onChange: h }),
631
+ /* @__PURE__ */ r.jsx(
632
+ ve,
633
+ {
634
+ photos: v,
635
+ photosNames: m,
636
+ onUpload: (w, P) => {
637
+ j(w), g(P);
638
+ },
639
+ onRemove: (w) => {
640
+ j(v.filter((P, O) => O !== w)), g(m.filter((P, O) => O !== w));
641
+ },
642
+ onPhotoClick: (w) => {
643
+ A(w), C(!0);
644
+ }
645
+ }
646
+ ),
647
+ /* @__PURE__ */ r.jsxs("div", { className: "mt-4 flex justify-between", children: [
648
+ /* @__PURE__ */ r.jsx(
649
+ "button",
650
+ {
651
+ onClick: () => i(t.id),
652
+ className: "rounded px-3 py-2 text-sm font-medium text-red-600 hover:bg-red-50",
653
+ children: n.delete
654
+ }
655
+ ),
656
+ /* @__PURE__ */ r.jsxs("div", { className: "space-x-2", children: [
657
+ /* @__PURE__ */ r.jsx(
658
+ "button",
659
+ {
660
+ onClick: o,
661
+ className: "rounded border border-gray-300 px-3 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50",
662
+ children: n.cancel
663
+ }
664
+ ),
665
+ /* @__PURE__ */ r.jsx(
666
+ "button",
667
+ {
668
+ onClick: _,
669
+ className: "rounded bg-blue-600 px-3 py-2 text-sm font-medium text-white hover:bg-blue-700",
670
+ children: n.save
671
+ }
672
+ )
673
+ ] })
674
+ ] })
675
+ ] })
676
+ ] }) }),
677
+ E && /* @__PURE__ */ r.jsx(
678
+ we,
679
+ {
680
+ photos: v,
681
+ initialIndex: p,
682
+ onClose: () => C(!1)
683
+ }
684
+ )
685
+ ] });
686
+ }, ke = ({
687
+ src: t,
688
+ markers: u,
689
+ onAddMarker: i,
690
+ onUpdateMarker: o,
691
+ onDeleteMarker: n
692
+ }) => {
693
+ const s = T(null), x = T(null), [c, d] = N(null), [f, h] = N(null), v = (m) => {
694
+ if (!x.current) return;
695
+ if (c) {
696
+ d(null);
697
+ return;
698
+ }
699
+ if (f) {
700
+ h(null);
701
+ return;
702
+ }
703
+ const { x: g, y: E } = ge(m, x.current), C = {
704
+ id: crypto.randomUUID(),
705
+ x: g,
706
+ y: E,
707
+ color: 16711680,
708
+ // Default Red
709
+ markerTitle: "",
710
+ note: "",
711
+ photos: [],
712
+ photosName: []
713
+ };
714
+ h(C);
715
+ }, j = u.find((m) => m.id === c);
716
+ return /* @__PURE__ */ r.jsx("div", { className: "relative inline-block overflow-hidden rounded-lg shadow-lg group select-none", children: /* @__PURE__ */ r.jsxs(
717
+ "div",
718
+ {
719
+ ref: x,
720
+ onClick: v,
721
+ className: "relative cursor-crosshair",
722
+ children: [
723
+ /* @__PURE__ */ r.jsx(
724
+ "img",
725
+ {
726
+ ref: s,
727
+ src: t,
728
+ alt: "Marker Area",
729
+ className: "block max-w-full h-auto",
730
+ draggable: !1
731
+ }
732
+ ),
733
+ u.map((m) => /* @__PURE__ */ r.jsx(
734
+ ne,
735
+ {
736
+ marker: m,
737
+ onClick: (g) => {
738
+ h(null), d(g.id);
739
+ },
740
+ onUpdate: (g, E) => o(g, E),
741
+ onDelete: n
742
+ },
743
+ m.id
744
+ )),
745
+ f && /* @__PURE__ */ r.jsx(
746
+ ne,
747
+ {
748
+ marker: f,
749
+ onClick: () => {
750
+ },
751
+ onUpdate: (m, g) => h((E) => E ? { ...E, ...g } : null),
752
+ onDelete: () => h(null)
753
+ },
754
+ "temp-marker"
755
+ ),
756
+ j && /* @__PURE__ */ r.jsx(
757
+ oe,
758
+ {
759
+ marker: j,
760
+ onSave: o,
761
+ onDelete: (m) => {
762
+ n(m), d(null);
763
+ },
764
+ onClose: () => d(null)
765
+ }
766
+ ),
767
+ f && /* @__PURE__ */ r.jsx(
768
+ oe,
769
+ {
770
+ marker: f,
771
+ onSave: (m, g) => {
772
+ i({ ...f, ...g }), h(null);
773
+ },
774
+ onDelete: () => h(null),
775
+ onClose: () => h(null)
776
+ }
777
+ )
778
+ ]
779
+ }
780
+ ) });
781
+ }, je = (t = []) => {
782
+ const [u, i] = N(t), o = M((c) => {
783
+ i((d) => [...d, c]);
784
+ }, []), n = M((c, d) => {
785
+ i(
786
+ (f) => f.map((h) => h.id === c ? { ...h, ...d } : h)
787
+ );
788
+ }, []), s = M((c) => {
789
+ i((d) => d.filter((f) => f.id !== c));
790
+ }, []), x = M((c) => {
791
+ i(c);
792
+ }, []);
793
+ return {
794
+ markers: u,
795
+ addMarker: o,
796
+ updateMarker: n,
797
+ removeMarker: s,
798
+ setAllMarkers: x
799
+ };
800
+ };
801
+ export {
802
+ pe as ColorPicker,
803
+ ke as ImageArea,
804
+ ke as ImageMarker,
805
+ we as Lightbox,
806
+ ne as MarkerItem,
807
+ oe as MarkerPopup,
808
+ ve as PhotoUploader,
809
+ V as useLocale,
810
+ je as useMarkerState
811
+ };
@@ -0,0 +1,22 @@
1
+ (function(N,n){typeof exports=="object"&&typeof module<"u"?n(exports,require("react")):typeof define=="function"&&define.amd?define(["exports","react"],n):(N=typeof globalThis<"u"?globalThis:N||self,n(N.ReactImageMarker={},N.React))})(this,(function(N,n){"use strict";var F={exports:{}},A={};/**
2
+ * @license React
3
+ * react-jsx-runtime.production.js
4
+ *
5
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
6
+ *
7
+ * This source code is licensed under the MIT license found in the
8
+ * LICENSE file in the root directory of this source tree.
9
+ */var H;function ce(){if(H)return A;H=1;var t=Symbol.for("react.transitional.element"),d=Symbol.for("react.fragment");function c(a,o,l){var g=null;if(l!==void 0&&(g=""+l),o.key!==void 0&&(g=""+o.key),"key"in o){l={};for(var u in o)u!=="key"&&(l[u]=o[u])}else l=o;return o=l.ref,{$$typeof:t,type:a,key:g,ref:o!==void 0?o:null,props:l}}return A.Fragment=d,A.jsx=c,A.jsxs=c,A}var O={};/**
10
+ * @license React
11
+ * react-jsx-runtime.development.js
12
+ *
13
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
14
+ *
15
+ * This source code is licensed under the MIT license found in the
16
+ * LICENSE file in the root directory of this source tree.
17
+ */var Z;function ue(){return Z||(Z=1,process.env.NODE_ENV!=="production"&&(function(){function t(e){if(e==null)return null;if(typeof e=="function")return e.$$typeof===ge?null:e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case P:return"Fragment";case S:return"Profiler";case b:return"StrictMode";case k:return"Suspense";case _:return"SuspenseList";case xe:return"Activity"}if(typeof e=="object")switch(typeof e.tag=="number"&&console.error("Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."),e.$$typeof){case R:return"Portal";case C:return e.displayName||"Context";case i:return(e._context.displayName||"Context")+".Consumer";case T:var s=e.render;return e=e.displayName,e||(e=s.displayName||s.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case I:return s=e.displayName||null,s!==null?s:t(e.type)||"Memo";case W:s=e._payload,e=e._init;try{return t(e(s))}catch{}}return null}function d(e){return""+e}function c(e){try{d(e);var s=!1}catch{s=!0}if(s){s=console;var v=s.error,y=typeof Symbol=="function"&&Symbol.toStringTag&&e[Symbol.toStringTag]||e.constructor.name||"Object";return v.call(s,"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",y),d(e)}}function a(e){if(e===P)return"<>";if(typeof e=="object"&&e!==null&&e.$$typeof===W)return"<...>";try{var s=t(e);return s?"<"+s+">":"<...>"}catch{return"<...>"}}function o(){var e=X.A;return e===null?null:e.getOwner()}function l(){return Error("react-stack-top-frame")}function g(e){if(ne.call(e,"key")){var s=Object.getOwnPropertyDescriptor(e,"key").get;if(s&&s.isReactWarning)return!1}return e.key!==void 0}function u(e,s){function v(){oe||(oe=!0,console.error("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",s))}v.isReactWarning=!0,Object.defineProperty(e,"key",{get:v,configurable:!0})}function f(){var e=t(this.type);return ae[e]||(ae[e]=!0,console.error("Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release.")),e=this.props.ref,e!==void 0?e:null}function m(e,s,v,y,L,G){var j=v.ref;return e={$$typeof:p,type:e,key:s,props:v,_owner:y},(j!==void 0?j:null)!==null?Object.defineProperty(e,"ref",{enumerable:!1,get:f}):Object.defineProperty(e,"ref",{enumerable:!1,value:null}),e._store={},Object.defineProperty(e._store,"validated",{configurable:!1,enumerable:!1,writable:!0,value:0}),Object.defineProperty(e,"_debugInfo",{configurable:!1,enumerable:!1,writable:!0,value:null}),Object.defineProperty(e,"_debugStack",{configurable:!1,enumerable:!1,writable:!0,value:L}),Object.defineProperty(e,"_debugTask",{configurable:!1,enumerable:!1,writable:!0,value:G}),Object.freeze&&(Object.freeze(e.props),Object.freeze(e)),e}function x(e,s,v,y,L,G){var j=s.children;if(j!==void 0)if(y)if(pe(j)){for(y=0;y<j.length;y++)w(j[y]);Object.freeze&&Object.freeze(j)}else console.error("React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead.");else w(j);if(ne.call(s,"key")){j=t(e);var M=Object.keys(s).filter(function(be){return be!=="key"});y=0<M.length?"{key: someKey, "+M.join(": ..., ")+": ...}":"{key: someKey}",ie[j+y]||(M=0<M.length?"{"+M.join(": ..., ")+": ...}":"{}",console.error(`A props object containing a "key" prop is being spread into JSX:
18
+ let props = %s;
19
+ <%s {...props} />
20
+ React keys must be passed directly to JSX without using spread:
21
+ let props = %s;
22
+ <%s key={someKey} {...props} />`,y,j,M,j),ie[j+y]=!0)}if(j=null,v!==void 0&&(c(v),j=""+v),g(s)&&(c(s.key),j=""+s.key),"key"in s){v={};for(var J in s)J!=="key"&&(v[J]=s[J])}else v=s;return j&&u(v,typeof e=="function"?e.displayName||e.name||"Unknown":e),m(e,j,v,o(),L,G)}function w(e){E(e)?e._store&&(e._store.validated=1):typeof e=="object"&&e!==null&&e.$$typeof===W&&(e._payload.status==="fulfilled"?E(e._payload.value)&&e._payload.value._store&&(e._payload.value._store.validated=1):e._store&&(e._store.validated=1))}function E(e){return typeof e=="object"&&e!==null&&e.$$typeof===p}var h=n,p=Symbol.for("react.transitional.element"),R=Symbol.for("react.portal"),P=Symbol.for("react.fragment"),b=Symbol.for("react.strict_mode"),S=Symbol.for("react.profiler"),i=Symbol.for("react.consumer"),C=Symbol.for("react.context"),T=Symbol.for("react.forward_ref"),k=Symbol.for("react.suspense"),_=Symbol.for("react.suspense_list"),I=Symbol.for("react.memo"),W=Symbol.for("react.lazy"),xe=Symbol.for("react.activity"),ge=Symbol.for("react.client.reference"),X=h.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,ne=Object.prototype.hasOwnProperty,pe=Array.isArray,V=console.createTask?console.createTask:function(){return null};h={react_stack_bottom_frame:function(e){return e()}};var oe,ae={},se=h.react_stack_bottom_frame.bind(h,l)(),le=V(a(l)),ie={};O.Fragment=P,O.jsx=function(e,s,v){var y=1e4>X.recentlyCreatedOwnerStacks++;return x(e,s,v,!1,y?Error("react-stack-top-frame"):se,y?V(a(e)):le)},O.jsxs=function(e,s,v){var y=1e4>X.recentlyCreatedOwnerStacks++;return x(e,s,v,!0,y?Error("react-stack-top-frame"):se,y?V(a(e)):le)}})()),O}var K;function de(){return K||(K=1,process.env.NODE_ENV==="production"?F.exports=ce():F.exports=ue()),F.exports}var r=de();const fe=(t,d)=>{const c=d.getBoundingClientRect();let a,o;"touches"in t?(a=t.touches[0].clientX,o=t.touches[0].clientY):(a=t.clientX,o=t.clientY);const l=(a-c.left)/c.width*100,g=(o-c.top)/c.height*100;return{x:Math.max(0,Math.min(100,l)),y:Math.max(0,Math.min(100,g))}},Y=t=>parseInt(t.replace(/^#/,""),16),Q=t=>{const d=t.toString(16).toUpperCase();return`#${"0".repeat(6-d.length)}${d}`},B={en:{editMarker:"Edit Marker",title:"Title",note:"Note",photos:"Photos",delete:"Delete",cancel:"Cancel",save:"Save",newMarker:"New Marker",uploadImage:"Upload Image",deleteConfirmation:"Are you sure you want to delete this marker?",confirmClear:"Clear existing markers for new image?",titlePlaceholder:"Enter a title",notePlaceholder:"Enter a note",titleRequired:"Title is required"},tr:{editMarker:"İşaretçiyi Düzenle",title:"Başlık",note:"Not",photos:"Fotoğraflar",delete:"Sil",cancel:"İptal",save:"Kaydet",newMarker:"Yeni İşaretçi",uploadImage:"Görsel Yükle",deleteConfirmation:"Bu işaretçiyi silmek istediğinizden emin misiniz?",confirmClear:"Yeni görsel için mevcut işaretçileri temizle?",titlePlaceholder:"Bir başlık girin",notePlaceholder:"Bir not girin",titleRequired:"Başlık zorunludur"},es:{editMarker:"Editar Marcador",title:"Título",note:"Nota",photos:"Fotos",delete:"Eliminar",cancel:"Cancelar",save:"Guardar",newMarker:"Nuevo Marcador",uploadImage:"Subir Imagen",deleteConfirmation:"¿Estás seguro de que deseas eliminar este marcador?",confirmClear:"¿Borrar marcadores existentes para la nueva imagen?",titlePlaceholder:"Ingrese un título",notePlaceholder:"Ingrese una nota",titleRequired:"El título es obligatorio"}},D=()=>n.useMemo(()=>{const d=navigator.language.split("-")[0];return d==="tr"?B.tr:d==="es"?B.es:B.en},[]),U=({marker:t,onClick:d,onUpdate:c,onDelete:a})=>{const o=Q(t.color),[l,g]=n.useState(!1),u=n.useRef(null),f=D(),m=n.useRef(null),x=n.useRef(!1),w=n.useRef({x:0,y:0}),E=n.useRef(!1),h=b=>{b.stopPropagation(),b.preventDefault(),E.current=!1,x.current=!1,w.current={x:b.clientX,y:b.clientY},m.current=window.setTimeout(()=>{x.current=!0,window.confirm(f.deleteConfirmation)&&a(t.id)},500),g(!0),b.target.setPointerCapture(b.pointerId)},p=b=>{var C;if(!l)return;Math.sqrt(Math.pow(b.clientX-w.current.x,2)+Math.pow(b.clientY-w.current.y,2))>5&&(E.current=!0,m.current&&(clearTimeout(m.current),m.current=null));const i=(C=u.current)==null?void 0:C.parentElement;if(i){const T=i.getBoundingClientRect();let k=(b.clientX-T.left)/T.width*100,_=(b.clientY-T.top)/T.height*100;k=Math.max(0,Math.min(100,k)),_=Math.max(0,Math.min(100,_)),c(t.id,{x:k,y:_})}},R=b=>{b.stopPropagation(),g(!1),b.target.releasePointerCapture(b.pointerId),m.current&&(clearTimeout(m.current),m.current=null)},P=b=>{b.stopPropagation(),!E.current&&!x.current&&d(t)};return r.jsx("div",{ref:u,onPointerDown:h,onPointerMove:p,onPointerUp:R,onClick:P,style:{left:`${t.x}%`,top:`${t.y}%`,backgroundColor:o,touchAction:"none"},className:`absolute h-6 w-6 -translate-x-1/2 -translate-y-1/2 cursor-pointer rounded-full border-2 border-white shadow-md flex items-center justify-center transition-transform ${l?"scale-125 z-50":"hover:scale-110"}`,children:r.jsx("div",{className:"w-2 h-2 bg-white rounded-full opacity-50"})})},q=[{name:"Red",hex:"#EF4444"},{name:"Orange",hex:"#F97316"},{name:"Amber",hex:"#F59E0B"},{name:"Yellow",hex:"#EAB308"},{name:"Lime",hex:"#84CC16"},{name:"Green",hex:"#22C55E"},{name:"Emerald",hex:"#10B981"},{name:"Teal",hex:"#14B8A6"},{name:"Cyan",hex:"#06B6D4"},{name:"Sky",hex:"#0EA5E9"},{name:"Blue",hex:"#3B82F6"},{name:"Indigo",hex:"#6366F1"},{name:"Violet",hex:"#8B5CF6"},{name:"Purple",hex:"#A855F7"},{name:"Fuchsia",hex:"#D946EF"},{name:"Pink",hex:"#EC4899"},{name:"Rose",hex:"#F43F5E"},{name:"Slate",hex:"#64748B"},{name:"Black",hex:"#000000"},{name:"White",hex:"#FFFFFF"}];Y(q[0].hex);const $=({color:t,onChange:d})=>{const c=Q(t);return r.jsxs("div",{className:"space-y-2",children:[r.jsx("label",{className:"block text-sm font-medium text-gray-700",children:"Color"}),r.jsxs("div",{className:"flex flex-wrap gap-2",children:[q.map(a=>r.jsx("button",{onClick:()=>d(Y(a.hex)),className:`h-6 w-6 rounded-full border transition-transform hover:scale-110 focus:outline-none focus:ring-2 focus:ring-offset-1 ${c===a.hex?"ring-2 ring-blue-500 ring-offset-1":"border-gray-200"}`,style:{backgroundColor:a.hex},title:a.name},a.hex)),r.jsx("input",{type:"color",value:c,onChange:a=>d(Y(a.target.value)),className:"h-6 w-6 cursor-pointer rounded-full border border-gray-300 p-0 overflow-hidden",title:"Custom Color"})]})]})},me=t=>new Promise((d,c)=>{const a=new FileReader;a.readAsDataURL(t),a.onload=()=>d(a.result),a.onerror=o=>c(o)}),ee=({photos:t,photosNames:d,onUpload:c,onRemove:a,onPhotoClick:o})=>{const l=D(),g=async u=>{if(u.target.files){const f=Array.from(u.target.files),m=await Promise.all(f.map(me)),x=f.map(p=>p.name),w=3-t.length,E=m.slice(0,w),h=x.slice(0,w);c([...t,...E],[...d,...h])}};return r.jsxs("div",{className:"space-y-2",children:[r.jsx("div",{className:"flex items-center justify-between",children:r.jsxs("label",{className:"text-sm font-medium text-gray-700",children:[l.photos," (",t.length,"/3)"]})}),r.jsxs("div",{className:"flex flex-wrap gap-2",children:[t.map((u,f)=>r.jsxs("div",{className:"relative h-16 w-16 group",children:[r.jsx("img",{src:u,alt:`Upload ${f}`,className:"h-full w-full object-cover rounded border cursor-pointer",onClick:()=>o&&o(f)}),r.jsx("button",{onClick:()=>a(f),className:"absolute -top-1 -right-1 bg-red-500 text-white rounded-full p-0.5 w-4 h-4 flex items-center justify-center text-xs opacity-0 group-hover:opacity-100 transition-opacity",children:"×"})]},f)),t.length<3&&r.jsxs("label",{className:"flex h-16 w-16 cursor-pointer items-center justify-center rounded border-2 border-dashed border-gray-300 hover:border-blue-500 hover:bg-blue-50 transition-colors",children:[r.jsx("span",{className:"text-xl text-gray-400",children:"+"}),r.jsx("input",{type:"file",accept:"image/*",multiple:!0,onChange:g,className:"hidden"})]})]})]})},te=({photos:t,initialIndex:d,onClose:c})=>{const[a,o]=n.useState(d),[l,g]=n.useState(1),[u,f]=n.useState({x:0,y:0}),[m,x]=n.useState(!1),w=n.useRef({x:0,y:0}),E=n.useRef(null),h=n.useCallback(()=>{g(1),f({x:0,y:0})},[]),p=n.useCallback(()=>{o(i=>(i+1)%t.length),h()},[t.length,h]),R=n.useCallback(()=>{o(i=>(i-1+t.length)%t.length),h()},[t.length,h]);n.useEffect(()=>{const i=C=>{C.key==="Escape"&&c(),C.key==="ArrowRight"&&p(),C.key==="ArrowLeft"&&R()};return window.addEventListener("keydown",i),()=>window.removeEventListener("keydown",i)},[c,p,R]),n.useEffect(()=>{const i=E.current;if(!i)return;const C=T=>{T.preventDefault();const k=-T.deltaY*.01,_=Math.min(Math.max(1,l+k),5);g(_),_===1&&f({x:0,y:0})};return i.addEventListener("wheel",C,{passive:!1}),()=>i.removeEventListener("wheel",C)},[l]);const P=i=>{l>1&&(i.preventDefault(),x(!0),w.current={x:i.clientX-u.x,y:i.clientY-u.y},i.target.setPointerCapture(i.pointerId))},b=i=>{m&&l>1&&(i.preventDefault(),f({x:i.clientX-w.current.x,y:i.clientY-w.current.y}))},S=i=>{x(!1),i.target.releasePointerCapture(i.pointerId)};return t.length?r.jsxs("div",{className:"fixed inset-0 z-[60] flex items-center justify-center bg-black bg-opacity-90 overflow-hidden",onClick:i=>{i.stopPropagation(),c()},children:[r.jsx("button",{onClick:c,className:"absolute top-4 right-4 text-white hover:text-gray-300 z-50 p-2",children:r.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",className:"h-8 w-8",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",children:r.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M6 18L18 6M6 6l12 12"})})}),r.jsxs("div",{ref:E,className:"relative flex items-center justify-center w-full h-full p-4",onClick:i=>i.stopPropagation(),children:[t.length>1&&r.jsx("button",{onClick:R,className:"absolute left-4 z-40 p-2 text-white hover:bg-white/10 rounded-full transition-colors",children:r.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",className:"h-10 w-10",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",children:r.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M15 19l-7-7 7-7"})})}),r.jsx("div",{className:"relative overflow-hidden flex items-center justify-center w-full h-full",children:r.jsx("img",{src:t[a],alt:`View ${a+1}`,onPointerDown:P,onPointerMove:b,onPointerUp:S,style:{transform:`scale(${l}) translate(${u.x/l}px, ${u.y/l}px)`,transition:m?"none":"transform 0.1s ease-out",cursor:l>1?m?"grabbing":"grab":"default",maxHeight:"100%",maxWidth:"100%"},className:"object-contain select-none touch-none"})}),t.length>1&&r.jsx("button",{onClick:p,className:"absolute right-4 z-40 p-2 text-white hover:bg-white/10 rounded-full transition-colors",children:r.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",className:"h-10 w-10",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",children:r.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M9 5l7 7-7 7"})})}),r.jsxs("div",{className:"absolute bottom-4 left-0 right-0 text-center text-white text-sm bg-black/50 py-1 pointer-events-none",children:[a+1," / ",t.length," ",l>1&&`(${Math.round(l*100)}%)`]})]})]}):null},z=({marker:t,onSave:d,onDelete:c,onClose:a})=>{const o=D(),[l,g]=n.useState(t.markerTitle),[u,f]=n.useState(t.note),[m,x]=n.useState(t.color),[w,E]=n.useState(t.photos),[h,p]=n.useState(t.photosName||[]),[R,P]=n.useState(!1),[b,S]=n.useState(0),[i,C]=n.useState(!1);n.useEffect(()=>{g(t.markerTitle),f(t.note),x(t.color),E(t.photos),p(t.photosName||[]),C(!1)},[t]);const T=()=>{if(!l.trim()){C(!0);return}d(t.id,{markerTitle:l,note:u,color:m,photos:w,photosName:h}),a()};return r.jsxs(r.Fragment,{children:[r.jsx("div",{className:"fixed inset-0 z-40 flex items-center justify-center bg-black/50",onClick:k=>k.stopPropagation(),children:r.jsxs("div",{className:"w-72 max-h-[90vh] overflow-y-auto rounded-lg bg-white p-4 shadow-xl border border-gray-200 relative z-50",children:[r.jsxs("div",{className:"mb-4 flex items-center justify-between",children:[r.jsx("h3",{className:"text-lg font-semibold text-gray-900",children:o.editMarker}),r.jsx("button",{onClick:a,className:"text-gray-400 hover:text-gray-600",children:"✕"})]}),r.jsxs("div",{className:"space-y-4",children:[r.jsxs("div",{children:[r.jsxs("label",{className:"block text-sm font-medium text-gray-700",children:[o.title," ",r.jsx("span",{className:"text-red-500",children:"*"})]}),r.jsx("input",{type:"text",value:l,placeholder:o.titlePlaceholder,onChange:k=>{g(k.target.value),i&&C(!1)},className:`mt-1 block w-full rounded-md border px-3 py-2 shadow-sm focus:outline-none focus:ring-1 sm:text-sm ${i?"border-red-500 focus:border-red-500 focus:ring-red-500":"border-gray-300 focus:border-blue-500 focus:ring-blue-500"}`}),i&&r.jsx("p",{className:"mt-1 text-xs text-red-500",children:o.titleRequired})]}),r.jsxs("div",{children:[r.jsx("label",{className:"block text-sm font-medium text-gray-700",children:o.note}),r.jsx("textarea",{value:u,placeholder:o.notePlaceholder,onChange:k=>f(k.target.value),rows:3,className:"mt-1 block w-full rounded-md border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500 sm:text-sm"})]}),r.jsx($,{color:m,onChange:x}),r.jsx(ee,{photos:w,photosNames:h,onUpload:(k,_)=>{E(k),p(_)},onRemove:k=>{E(w.filter((_,I)=>I!==k)),p(h.filter((_,I)=>I!==k))},onPhotoClick:k=>{S(k),P(!0)}}),r.jsxs("div",{className:"mt-4 flex justify-between",children:[r.jsx("button",{onClick:()=>c(t.id),className:"rounded px-3 py-2 text-sm font-medium text-red-600 hover:bg-red-50",children:o.delete}),r.jsxs("div",{className:"space-x-2",children:[r.jsx("button",{onClick:a,className:"rounded border border-gray-300 px-3 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50",children:o.cancel}),r.jsx("button",{onClick:T,className:"rounded bg-blue-600 px-3 py-2 text-sm font-medium text-white hover:bg-blue-700",children:o.save})]})]})]})]})}),R&&r.jsx(te,{photos:w,initialIndex:b,onClose:()=>P(!1)})]})},re=({src:t,markers:d,onAddMarker:c,onUpdateMarker:a,onDeleteMarker:o})=>{const l=n.useRef(null),g=n.useRef(null),[u,f]=n.useState(null),[m,x]=n.useState(null),w=h=>{if(!g.current)return;if(u){f(null);return}if(m){x(null);return}const{x:p,y:R}=fe(h,g.current),P={id:crypto.randomUUID(),x:p,y:R,color:16711680,markerTitle:"",note:"",photos:[],photosName:[]};x(P)},E=d.find(h=>h.id===u);return r.jsx("div",{className:"relative inline-block overflow-hidden rounded-lg shadow-lg group select-none",children:r.jsxs("div",{ref:g,onClick:w,className:"relative cursor-crosshair",children:[r.jsx("img",{ref:l,src:t,alt:"Marker Area",className:"block max-w-full h-auto",draggable:!1}),d.map(h=>r.jsx(U,{marker:h,onClick:p=>{x(null),f(p.id)},onUpdate:(p,R)=>a(p,R),onDelete:o},h.id)),m&&r.jsx(U,{marker:m,onClick:()=>{},onUpdate:(h,p)=>x(R=>R?{...R,...p}:null),onDelete:()=>x(null)},"temp-marker"),E&&r.jsx(z,{marker:E,onSave:a,onDelete:h=>{o(h),f(null)},onClose:()=>f(null)}),m&&r.jsx(z,{marker:m,onSave:(h,p)=>{c({...m,...p}),x(null)},onDelete:()=>x(null),onClose:()=>x(null)})]})})},he=(t=[])=>{const[d,c]=n.useState(t),a=n.useCallback(u=>{c(f=>[...f,u])},[]),o=n.useCallback((u,f)=>{c(m=>m.map(x=>x.id===u?{...x,...f}:x))},[]),l=n.useCallback(u=>{c(f=>f.filter(m=>m.id!==u))},[]),g=n.useCallback(u=>{c(u)},[]);return{markers:d,addMarker:a,updateMarker:o,removeMarker:l,setAllMarkers:g}};N.ColorPicker=$,N.ImageArea=re,N.ImageMarker=re,N.Lightbox=te,N.MarkerItem=U,N.MarkerPopup=z,N.PhotoUploader=ee,N.useLocale=D,N.useMarkerState=he,Object.defineProperty(N,Symbol.toStringTag,{value:"Module"})}));
package/dist/vite.svg ADDED
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "react-image-marker-viewer",
3
+ "private": false,
4
+ "version": "0.1.0",
5
+ "type": "module",
6
+ "main": "./dist/react-image-marker.umd.js",
7
+ "module": "./dist/react-image-marker.es.js",
8
+ "types": "./dist/index.d.ts",
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "import": "./dist/react-image-marker.es.js",
16
+ "require": "./dist/react-image-marker.umd.js"
17
+ }
18
+ },
19
+ "scripts": {
20
+ "dev": "vite",
21
+ "build": "tsc -b && vite build",
22
+ "lint": "eslint .",
23
+ "preview": "vite preview"
24
+ },
25
+ "peerDependencies": {
26
+ "react": ">=16.8.0",
27
+ "react-dom": ">=16.8.0"
28
+ },
29
+ "devDependencies": {
30
+ "@eslint/js": "^9.39.1",
31
+ "@types/node": "^24.10.1",
32
+ "@types/react": "^19.2.7",
33
+ "@types/react-dom": "^19.2.3",
34
+ "@vitejs/plugin-react": "^5.1.1",
35
+ "autoprefixer": "^10.4.24",
36
+ "eslint": "^9.39.1",
37
+ "eslint-plugin-react-hooks": "^7.0.1",
38
+ "eslint-plugin-react-refresh": "^0.4.24",
39
+ "globals": "^16.5.0",
40
+ "postcss": "^8.5.6",
41
+ "tailwindcss": "^3.4.17",
42
+ "typescript": "~5.9.3",
43
+ "typescript-eslint": "^8.48.0",
44
+ "vite": "^6.0.7",
45
+ "vite-plugin-dts": "^4.5.4",
46
+ "react": "^19.2.7",
47
+ "react-dom": "^19.2.7"
48
+ }
49
+ }