소스 검색

feat: additional valve input types

Timothy Jaeryang Baek 3 달 전
부모
커밋
f55e93cabe
4개의 변경된 파일140개의 추가작업 그리고 1개의 파일을 삭제
  1. 7 0
      package-lock.json
  2. 1 0
      package.json
  3. 58 1
      src/lib/components/common/Valves.svelte
  4. 74 0
      src/lib/components/common/Valves/MapSelector.svelte

+ 7 - 0
package-lock.json

@@ -54,6 +54,7 @@
 				"jspdf": "^3.0.0",
 				"katex": "^0.16.22",
 				"kokoro-js": "^1.1.1",
+				"leaflet": "^1.9.4",
 				"marked": "^9.1.0",
 				"mermaid": "^11.6.0",
 				"paneforge": "^0.0.6",
@@ -8065,6 +8066,12 @@
 				"node": ">=10.13.0"
 			}
 		},
+		"node_modules/leaflet": {
+			"version": "1.9.4",
+			"resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz",
+			"integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==",
+			"license": "BSD-2-Clause"
+		},
 		"node_modules/levn": {
 			"version": "0.4.1",
 			"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",

+ 1 - 0
package.json

@@ -98,6 +98,7 @@
 		"jspdf": "^3.0.0",
 		"katex": "^0.16.22",
 		"kokoro-js": "^1.1.1",
+		"leaflet": "^1.9.4",
 		"marked": "^9.1.0",
 		"mermaid": "^11.6.0",
 		"paneforge": "^0.0.6",

+ 58 - 1
src/lib/components/common/Valves.svelte

@@ -4,6 +4,8 @@
 	const i18n = getContext('i18n');
 
 	import Switch from './Switch.svelte';
+	import MapSelector from './Valves/MapSelector.svelte';
+	import { split } from 'postcss/lib/list';
 
 	export let valvesSpec = null;
 	export let valves = {};
@@ -49,7 +51,7 @@
 
 			{#if (valves[property] ?? null) !== null}
 				<!-- {valves[property]} -->
-				<div class="flex mt-0.5 mb-1.5 space-x-2">
+				<div class="flex mt-0.5 mb-0.5 space-x-2">
 					<div class=" flex-1">
 						{#if valvesSpec.properties[property]?.enum ?? null}
 							<select
@@ -92,6 +94,61 @@
 									dispatch('change');
 								}}
 							/>
+						{:else if valvesSpec.properties[property]?.input ?? null}
+							{#if valvesSpec.properties[property]?.input?.type === 'color'}
+								<div class="flex items-center space-x-2">
+									<div class="relative size-6">
+										<input
+											type="color"
+											class="size-6 rounded cursor-pointer border border-gray-200 dark:border-gray-700"
+											value={valves[property] ?? '#000000'}
+											on:input={(e) => {
+												// Convert the color value to uppercase immediately
+												valves[property] = e.target.value.toUpperCase();
+												dispatch('change');
+											}}
+										/>
+									</div>
+
+									<input
+										type="text"
+										class="flex-1 rounded-lg py-2 text-sm dark:text-gray-300 dark:bg-gray-850 outline-hidden border border-gray-100 dark:border-gray-850"
+										placeholder="Enter hex color (e.g. #FF0000)"
+										bind:value={valves[property]}
+										autocomplete="off"
+										disabled
+										on:change={() => {
+											dispatch('change');
+										}}
+									/>
+								</div>
+							{:else if valvesSpec.properties[property]?.input?.type === 'map'}
+								<!-- EXPERIMENTAL INPUT TYPE, DO NOT USE IN PRODUCTION -->
+								<div class="flex flex-col items-center gap-1">
+									<MapSelector
+										setViewLocation={((valves[property] ?? '').includes(',') ?? false)
+											? valves[property].split(',')
+											: null}
+										onClick={(value) => {
+											valves[property] = value;
+											dispatch('change');
+										}}
+									/>
+
+									{#if valves[property]}
+										<input
+											type="text"
+											class=" w-full rounded-lg py-1 text-left text-sm dark:text-gray-300 dark:bg-gray-850 outline-hidden border border-gray-100 dark:border-gray-850"
+											placeholder="Enter coordinates (e.g. 51.505, -0.09)"
+											bind:value={valves[property]}
+											autocomplete="off"
+											on:change={() => {
+												dispatch('change');
+											}}
+										/>
+									{/if}
+								</div>
+							{/if}
 						{:else}
 							<textarea
 								class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-hidden border border-gray-100 dark:border-gray-850"

+ 74 - 0
src/lib/components/common/Valves/MapSelector.svelte

@@ -0,0 +1,74 @@
+<script>
+	import L from 'leaflet';
+	import 'leaflet/dist/leaflet.css';
+	import { onMount, onDestroy } from 'svelte';
+
+	let map;
+	let mapElement;
+
+	export let setViewLocation = [51.505, -0.09];
+	export let points = [];
+
+	export let onClick = (e) => {};
+
+	let markerGroupLayer = null;
+
+	const setMarkers = (points) => {
+		if (map) {
+			if (markerGroupLayer) {
+				map.removeLayer(markerGroupLayer);
+			}
+
+			let markers = [];
+			for (let point of points) {
+				const marker = L.marker(point.coords).bindPopup(point.content);
+
+				markers.push(marker);
+			}
+
+			markerGroupLayer = L.featureGroup(markers).addTo(map);
+
+			try {
+				map.fitBounds(markerGroupLayer.getBounds(), {
+					maxZoom: 13
+				});
+			} catch (error) {
+				console.error('Error fitting bounds for markers:', error);
+			}
+		}
+	};
+
+	onMount(async () => {
+		map = L.map(mapElement).setView(setViewLocation ? setViewLocation : [51.505, -0.09], 10);
+
+		L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
+			attribution:
+				'&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
+		}).addTo(map);
+
+		setMarkers(points);
+
+		map.on('click', (e) => {
+			console.log(e.latlng);
+			onClick(`${e.latlng.lat}, ${e.latlng.lng}`);
+
+			setMarkers([
+				{
+					coords: [e.latlng.lat, e.latlng.lng],
+					content: `Lat: ${e.latlng.lat}, Lng: ${e.latlng.lng}`
+				}
+			]);
+		});
+	});
+
+	onDestroy(async () => {
+		if (map) {
+			console.log('Unloading Leaflet map.');
+			map.remove();
+		}
+	});
+</script>
+
+<div class=" z-10 w-full">
+	<div bind:this={mapElement} class="h-96 z-10" />
+</div>