| 
									
										
										
										
											2023-04-26 18:04:42 +02:00
										 |  |  | <script lang="ts"> | 
					
						
							|  |  |  |   import { Store, Stores, UIEventSource } from "../../../Logic/UIEventSource"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Given the available floors, shows an elevator to pick a single one | 
					
						
							|  |  |  |    *  | 
					
						
							|  |  |  |    * This is but the input element, the logic of handling the filter is in 'LevelSelector' | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   export let floors: Store<string[]>; | 
					
						
							|  |  |  |   export let value: UIEventSource<string>; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const HEIGHT = 40; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   let initialIndex = Math.max(0, floors?.data?.findIndex(f => f === value?.data) ?? 0); | 
					
						
							|  |  |  |   let index: UIEventSource<number> = new UIEventSource<number>(initialIndex); | 
					
						
							|  |  |  |   let forceIndex: number | undefined = undefined; | 
					
						
							|  |  |  |   let top = Math.max(0, initialIndex) * HEIGHT; | 
					
						
							|  |  |  |   let elevator: HTMLImageElement; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   let mouseDown = false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   let container: HTMLElement; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   $:{ | 
					
						
							|  |  |  |     if (top > 0 || forceIndex !== undefined) { | 
					
						
							|  |  |  |       index.setData(closestFloorIndex()); | 
					
						
							|  |  |  |       value.setData(floors.data[forceIndex ?? closestFloorIndex()]); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function unclick() { | 
					
						
							|  |  |  |     mouseDown = false; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function click() { | 
					
						
							|  |  |  |     mouseDown = true; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function closestFloorIndex() { | 
					
						
							|  |  |  |     return Math.min(floors.data.length - 1, Math.max(0, Math.round(top / HEIGHT))); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function onMove(e: { movementY: number }) { | 
					
						
							|  |  |  |     if (mouseDown) { | 
					
						
							|  |  |  |       forceIndex = undefined; | 
					
						
							|  |  |  |       const containerY = container.clientTop; | 
					
						
							|  |  |  |       const containerMax = containerY + (floors.data.length - 1) * HEIGHT; | 
					
						
							|  |  |  |       top = Math.min(Math.max(0, top + e.movementY), containerMax); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   let momentum = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function stabilize() { | 
					
						
							|  |  |  |     // Automatically move the elevator to the closes floor | 
					
						
							|  |  |  |     if (mouseDown) { | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     const target = (forceIndex ?? index.data) * HEIGHT; | 
					
						
							|  |  |  |     let diff = target - top; | 
					
						
							|  |  |  |     if (diff > 1) { | 
					
						
							|  |  |  |       diff /= 3; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     const sign = Math.sign(diff); | 
					
						
							|  |  |  |     momentum = momentum + sign; | 
					
						
							|  |  |  |     let diffR = Math.min(Math.abs(momentum), forceIndex !== undefined ? 9 : 3, Math.abs(diff)); | 
					
						
							|  |  |  |     momentum = Math.sign(momentum) * Math.min(diffR, Math.abs(momentum)); | 
					
						
							|  |  |  |     top += sign * diffR; | 
					
						
							|  |  |  |     if (index.data === forceIndex) { | 
					
						
							|  |  |  |       forceIndex = undefined; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-05-11 02:17:41 +02:00
										 |  |  |     top = Math.max(top, 0) | 
					
						
							| 
									
										
										
										
											2023-04-26 18:04:42 +02:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Stores.Chronic(50).addCallback(_ => stabilize()); | 
					
						
							| 
									
										
										
										
											2023-05-01 01:14:48 +02:00
										 |  |  |   floors.addCallback(floors => { | 
					
						
							|  |  |  |     forceIndex = floors.findIndex(s => s === value.data) | 
					
						
							|  |  |  |   }) | 
					
						
							| 
									
										
										
										
											2023-04-26 18:04:42 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   let image: HTMLImageElement; | 
					
						
							|  |  |  |   $:{ | 
					
						
							|  |  |  |     if (image) { | 
					
						
							|  |  |  |       let lastY = 0; | 
					
						
							|  |  |  |       image.ontouchstart = (e: TouchEvent) => { | 
					
						
							|  |  |  |         mouseDown = true; | 
					
						
							|  |  |  |         lastY = e.changedTouches[0].clientY; | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |       image.ontouchmove = e => { | 
					
						
							|  |  |  |         const y = e.changedTouches[0].clientY; | 
					
						
							|  |  |  |         console.log(y) | 
					
						
							|  |  |  |         const movementY = y - lastY; | 
					
						
							|  |  |  |         lastY = y; | 
					
						
							|  |  |  |         onMove({ movementY }); | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |       image.ontouchend = unclick; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | </script> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | <div bind:this={container} class="relative" | 
					
						
							|  |  |  |      style={`height: calc(${HEIGHT}px * ${$floors.length}); width: 96px`}> | 
					
						
							|  |  |  |   <div class="h-full absolute w-min right-0"> | 
					
						
							|  |  |  |     {#each $floors as floor, i} | 
					
						
							|  |  |  |       <button style={`height: ${HEIGHT}px; width: ${HEIGHT}px`} | 
					
						
							| 
									
										
										
										
											2023-05-11 02:17:41 +02:00
										 |  |  |               class={"m-0 border-2 border-gray-300 flex content-box justify-center items-center "+(i === (forceIndex ?? $index) ? "selected": "" ) | 
					
						
							| 
									
										
										
										
											2023-04-26 18:04:42 +02:00
										 |  |  |            } | 
					
						
							|  |  |  |               on:click={() => {forceIndex = i}} | 
					
						
							|  |  |  |       > {floor}</button> | 
					
						
							|  |  |  |     {/each} | 
					
						
							|  |  |  |   </div> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   <div style={`width: ${HEIGHT}px`}> | 
					
						
							|  |  |  |     <img bind:this={image} class="draggable" draggable="false" on:mousedown={click} src="./assets/svg/elevator.svg" | 
					
						
							|  |  |  |          style={" top: "+top+"px;"} /> | 
					
						
							|  |  |  |   </div> | 
					
						
							|  |  |  | </div> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | <svelte:window on:mousemove={onMove} on:mouseup={unclick} /> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | <style> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     .draggable { | 
					
						
							|  |  |  |         user-select: none; | 
					
						
							|  |  |  |         cursor: move; | 
					
						
							|  |  |  |         position: absolute; | 
					
						
							|  |  |  |         user-drag: none; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         height: 72px; | 
					
						
							|  |  |  |         margin-top: -15px; | 
					
						
							|  |  |  |         margin-bottom: -15px; | 
					
						
							|  |  |  |         margin-left: -18px; | 
					
						
							|  |  |  |         -webkit-user-drag: none; | 
					
						
							|  |  |  |         -moz-user-select: none; | 
					
						
							|  |  |  |         -webkit-user-select: none; | 
					
						
							|  |  |  |         -ms-user-select: none; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | </style> |