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