forked from pdsls.dev/pdsls
atproto explorer

drag create window

juli.ee 927a5355 cc168c52

verified
Changed files
+94 -8
src
components
+92 -7
src/components/create.tsx
··· 1 1 import { Client } from "@atcute/client"; 2 2 import { remove } from "@mary/exif-rm"; 3 3 import { useNavigate, useParams } from "@solidjs/router"; 4 - import { createSignal, onCleanup, Show } from "solid-js"; 4 + import { createSignal, onCleanup, onMount, Show } from "solid-js"; 5 5 import { Editor, editorView } from "../components/editor.jsx"; 6 6 import { agent } from "../components/login.jsx"; 7 7 import { setNotif } from "../layout.jsx"; ··· 128 128 } 129 129 }; 130 130 131 + const dragDiv = (box: HTMLDivElement) => { 132 + let currentBox: HTMLDivElement | null = null; 133 + let isDragging = false; 134 + let offsetX: number; 135 + let offsetY: number; 136 + 137 + const handleMouseDown = (e: MouseEvent) => { 138 + if (!(e.target instanceof HTMLElement)) return; 139 + 140 + const closestDraggable = e.target.closest("[data-draggable]") as HTMLElement; 141 + if (closestDraggable && closestDraggable !== box) return; 142 + 143 + if ( 144 + ["INPUT", "SELECT", "BUTTON", "LABEL"].includes(e.target.tagName) || 145 + e.target.closest("#editor") 146 + ) 147 + return; 148 + 149 + e.preventDefault(); 150 + isDragging = true; 151 + box.classList.add("cursor-grabbing"); 152 + currentBox = box; 153 + 154 + const rect = box.getBoundingClientRect(); 155 + 156 + box.style.left = rect.left + "px"; 157 + box.style.top = rect.top + "px"; 158 + 159 + box.classList.remove("-translate-x-1/2"); 160 + 161 + offsetX = e.clientX - rect.left; 162 + offsetY = e.clientY - rect.top; 163 + }; 164 + 165 + const handleMouseMove = (e: MouseEvent) => { 166 + if (isDragging && box === currentBox) { 167 + let newLeft = e.clientX - offsetX; 168 + let newTop = e.clientY - offsetY; 169 + 170 + const boxWidth = box.offsetWidth; 171 + const boxHeight = box.offsetHeight; 172 + 173 + const viewportWidth = window.innerWidth; 174 + const viewportHeight = window.innerHeight; 175 + 176 + newLeft = Math.max(0, Math.min(newLeft, viewportWidth - boxWidth)); 177 + newTop = Math.max(0, Math.min(newTop, viewportHeight - boxHeight)); 178 + 179 + box.style.left = newLeft + "px"; 180 + box.style.top = newTop + "px"; 181 + } 182 + }; 183 + 184 + const handleMouseUp = () => { 185 + if (isDragging && box === currentBox) { 186 + isDragging = false; 187 + box.classList.remove("cursor-grabbing"); 188 + currentBox = null; 189 + } 190 + }; 191 + 192 + onMount(() => { 193 + box.addEventListener("mousedown", handleMouseDown); 194 + document.addEventListener("mousemove", handleMouseMove); 195 + document.addEventListener("mouseup", handleMouseUp); 196 + }); 197 + 198 + onCleanup(() => { 199 + box.removeEventListener("mousedown", handleMouseDown); 200 + document.removeEventListener("mousemove", handleMouseMove); 201 + document.removeEventListener("mouseup", handleMouseUp); 202 + }); 203 + }; 204 + 131 205 const FileUpload = (props: { file: File }) => { 132 206 const [uploading, setUploading] = createSignal(false); 133 207 const [error, setError] = createSignal(""); ··· 175 249 }; 176 250 177 251 return ( 178 - <div class="dark:bg-dark-300 dark:shadow-dark-700 absolute top-70 left-[50%] w-[20rem] -translate-x-1/2 rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-4 shadow-md transition-opacity duration-200 dark:border-neutral-700 starting:opacity-0"> 252 + <div 253 + data-draggable 254 + class="dark:bg-dark-300 dark:shadow-dark-700 absolute top-70 left-[50%] w-[20rem] -translate-x-1/2 rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-4 shadow-md transition-opacity duration-200 dark:border-neutral-700 starting:opacity-0" 255 + ref={dragDiv} 256 + > 179 257 <h2 class="mb-2 font-semibold">Upload blob</h2> 180 258 <div class="flex flex-col gap-2 text-sm"> 181 259 <div class="flex flex-col gap-1"> ··· 229 307 return ( 230 308 <> 231 309 <Modal open={openDialog()} onClose={() => setOpenDialog(false)} closeOnClick={false}> 232 - <div class="dark:bg-dark-300 dark:shadow-dark-700 absolute top-16 left-[50%] w-screen -translate-x-1/2 rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-4 shadow-md transition-opacity duration-200 sm:w-xl lg:w-[48rem] dark:border-neutral-700 starting:opacity-0"> 310 + <div 311 + data-draggable 312 + class="dark:bg-dark-300 dark:shadow-dark-700 absolute top-16 left-[50%] w-screen -translate-x-1/2 cursor-grab rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-4 shadow-md transition-opacity duration-200 sm:w-xl lg:w-[48rem] dark:border-neutral-700 starting:opacity-0" 313 + ref={dragDiv} 314 + > 233 315 <div class="mb-2 flex w-full justify-between"> 234 316 <div class="font-semibold"> 235 - <span>{props.create ? "Creating" : "Editing"} record</span> 317 + <span class="select-none">{props.create ? "Creating" : "Editing"} record</span> 236 318 </div> 237 319 <button 238 320 onclick={() => setOpenDialog(false)} ··· 274 356 <select 275 357 name="validate" 276 358 id="validate" 277 - class="dark:bg-dark-100 dark:shadow-dark-700 rounded-lg border-[0.5px] border-neutral-300 bg-white px-1 py-1 shadow-xs focus:outline-[1px] focus:outline-neutral-600 dark:border-neutral-600 dark:focus:outline-neutral-400" 359 + class="dark:bg-dark-100 dark:shadow-dark-700 rounded-lg border-[0.5px] border-neutral-300 bg-white px-1 py-1 shadow-xs select-none focus:outline-[1px] focus:outline-neutral-600 dark:border-neutral-600 dark:focus:outline-neutral-400" 278 360 > 279 361 <option value="unset">Unset</option> 280 362 <option value="true">True</option> ··· 296 378 <div class="text-sm text-red-500 dark:text-red-400">{notice()}</div> 297 379 </Show> 298 380 <div class="flex justify-between gap-2"> 299 - <div class="dark:hover:bg-dark-200 dark:shadow-dark-700 dark:active:bg-dark-100 flex w-fit rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 text-xs shadow-xs hover:bg-neutral-100 active:bg-neutral-200 dark:border-neutral-700 dark:bg-neutral-800"> 381 + <button 382 + type="button" 383 + class="dark:hover:bg-dark-200 dark:shadow-dark-700 dark:active:bg-dark-100 flex w-fit rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 text-xs shadow-xs hover:bg-neutral-100 active:bg-neutral-200 dark:border-neutral-700 dark:bg-neutral-800" 384 + > 300 385 <input 301 386 type="file" 302 387 id="blob" ··· 310 395 <span class="iconify lucide--upload"></span> 311 396 Upload 312 397 </label> 313 - </div> 398 + </button> 314 399 <Modal 315 400 open={openUpload()} 316 401 onClose={() => setOpenUpload(false)}
+2 -1
src/components/editor.tsx
··· 57 57 return ( 58 58 <div 59 59 ref={editorDiv} 60 - class="dark:shadow-dark-700 border-[0.5px] border-neutral-300 shadow-xs dark:border-neutral-700" 60 + id="editor" 61 + class="dark:shadow-dark-700 cursor-auto border-[0.5px] border-neutral-300 shadow-xs dark:border-neutral-700" 61 62 ></div> 62 63 ); 63 64 };