main
html 165 lines 5.34 KB
Raw
1 <html>
2
3 <head>
4 <title>Image Viewer</title>
5
6 <script type="module">
7 import { store } from "/components/modals/image-viewer/image-viewer-store.js";
8 </script>
9 </head>
10
11 <body>
12 <div x-data>
13 <template x-if="$store.imageViewer">
14 <div id="image-viewer-wrapper" class="image-modal-container">
15 <!-- Image canvas for panning -->
16 <div class="image-canvas"
17 @pointerdown="$store.imageViewer.startDrag($event)"
18 @pointermove="$store.imageViewer.onDrag($event)"
19 @wheel="$store.imageViewer.onWheel($event)"
20 @pointerup="$store.imageViewer.endDrag()"
21 @pointercancel="$store.imageViewer.endDrag()">
22 <img
23 x-show="$store.imageViewer.currentImageUrl"
24 :src="$store.imageViewer.currentImageUrl"
25 :alt="$store.imageViewer.currentImageName || 'Image'"
26 class="modal-image"
27 :class="{ 'dragging': $store.imageViewer.isDragging }"
28 @load="$store.imageViewer.onImageLoad($event)"
29 @error="$store.imageViewer.imageError = true"
30 draggable="false"
31 />
32
33 <!-- Loading indicator -->
34 <div x-show="!$store.imageViewer.imageLoaded && !$store.imageViewer.imageError" class="loading-indicator">
35 <div class="loading-spinner"></div>
36 <p>Loading image...</p>
37 </div>
38
39 <!-- Error indicator -->
40 <div x-show="$store.imageViewer.imageError" class="error-indicator">
41 <p>Failed to load image</p>
42 </div>
43 </div>
44
45 <!-- Fixed zoom controls - OUTSIDE the canvas -->
46 <div class="zoom-controls">
47 <button type="button" @click="$store.imageViewer.zoomOut()" class="button zoom-btn">
48 <x-icon class="icon" name="zoom_out"></x-icon>
49 </button>
50 <button type="button" @click="$store.imageViewer.resetZoom()" class="button zoom-btn">
51 <x-icon class="icon" name="home"></x-icon>
52 </button>
53 <button type="button" @click="$store.imageViewer.zoomIn()" class="button zoom-btn">
54 <x-icon class="icon" name="zoom_in"></x-icon>
55 </button>
56 </div>
57 </div>
58 </template>
59 </div>
60
61 <style>
62 .image-modal-container {
63 width: 100%;
64 height: 70vh;
65 position: relative;
66 background: var(--color-bg-secondary);
67 display: flex;
68 align-items: center;
69 justify-content: center;
70 }
71
72 .image-canvas {
73 width: 100%;
74 height: 100%;
75 display: flex;
76 align-items: center;
77 justify-content: center;
78 cursor: grab;
79 overflow: hidden;
80 touch-action: none;
81 background: repeating-linear-gradient(45deg, var(--color-panel) 0 10px, var(--color-chat-background) 10px 20px);
82 }
83
84 .image-canvas:active {
85 cursor: grabbing;
86 }
87
88 .modal-image {
89 max-width: 100%;
90 max-height: 100%;
91 object-fit: contain;
92 user-select: none;
93 -webkit-user-drag: none;
94 pointer-events: none;
95 }
96
97 .modal-image.zoomed {
98 max-width: none;
99 max-height: none;
100 }
101
102 .loading-indicator, .error-indicator {
103 position: absolute;
104 display: flex;
105 flex-direction: column;
106 align-items: center;
107 justify-content: center;
108 color: var(--color-text-secondary);
109 }
110
111 .loading-spinner {
112 width: 40px;
113 height: 40px;
114 border: 3px solid var(--color-border);
115 border-top: 3px solid var(--color-primary);
116 border-radius: 50%;
117 animation: spin 1s linear infinite;
118 margin-bottom: 10px;
119 }
120
121 @keyframes spin {
122 0% { transform: rotate(0deg); }
123 100% { transform: rotate(360deg); }
124 }
125
126 .zoom-controls {
127 position: absolute;
128 bottom: 20px;
129 right: 20px;
130 display: flex;
131 align-items: center;
132 gap: 4px;
133 background: rgba(0, 0, 0, 0.6);
134 padding: 6px;
135 border-radius: 12px;
136 backdrop-filter: blur(5px);
137 z-index: 100;
138 }
139
140 .zoom-btn {
141 background: transparent;
142 border: none;
143 color: white;
144 cursor: pointer;
145 padding: 6px 10px;
146 border-radius: 6px;
147 font-size: 16px;
148 font-weight: 500;
149 min-width: 32px;
150 height: 32px;
151 display: flex;
152 align-items: center;
153 justify-content: center;
154 transition: background-color 0.2s ease;
155 }
156
157 .zoom-btn:hover {
158 background: rgba(255, 255, 255, 0.15);
159 }
160 </style>
161
162 </body>
163
164 </html>
165