mirror of
https://github.com/open-webui/open-webui.git
synced 2025-12-16 14:25:19 +00:00
feat: additional valve input types
This commit is contained in:
parent
7adb15d4b0
commit
f55e93cabe
4 changed files with 140 additions and 1 deletions
7
package-lock.json
generated
7
package-lock.json
generated
|
|
@ -54,6 +54,7 @@
|
||||||
"jspdf": "^3.0.0",
|
"jspdf": "^3.0.0",
|
||||||
"katex": "^0.16.22",
|
"katex": "^0.16.22",
|
||||||
"kokoro-js": "^1.1.1",
|
"kokoro-js": "^1.1.1",
|
||||||
|
"leaflet": "^1.9.4",
|
||||||
"marked": "^9.1.0",
|
"marked": "^9.1.0",
|
||||||
"mermaid": "^11.6.0",
|
"mermaid": "^11.6.0",
|
||||||
"paneforge": "^0.0.6",
|
"paneforge": "^0.0.6",
|
||||||
|
|
@ -8065,6 +8066,12 @@
|
||||||
"node": ">=10.13.0"
|
"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": {
|
"node_modules/levn": {
|
||||||
"version": "0.4.1",
|
"version": "0.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,7 @@
|
||||||
"jspdf": "^3.0.0",
|
"jspdf": "^3.0.0",
|
||||||
"katex": "^0.16.22",
|
"katex": "^0.16.22",
|
||||||
"kokoro-js": "^1.1.1",
|
"kokoro-js": "^1.1.1",
|
||||||
|
"leaflet": "^1.9.4",
|
||||||
"marked": "^9.1.0",
|
"marked": "^9.1.0",
|
||||||
"mermaid": "^11.6.0",
|
"mermaid": "^11.6.0",
|
||||||
"paneforge": "^0.0.6",
|
"paneforge": "^0.0.6",
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@
|
||||||
const i18n = getContext('i18n');
|
const i18n = getContext('i18n');
|
||||||
|
|
||||||
import Switch from './Switch.svelte';
|
import Switch from './Switch.svelte';
|
||||||
|
import MapSelector from './Valves/MapSelector.svelte';
|
||||||
|
import { split } from 'postcss/lib/list';
|
||||||
|
|
||||||
export let valvesSpec = null;
|
export let valvesSpec = null;
|
||||||
export let valves = {};
|
export let valves = {};
|
||||||
|
|
@ -49,7 +51,7 @@
|
||||||
|
|
||||||
{#if (valves[property] ?? null) !== null}
|
{#if (valves[property] ?? null) !== null}
|
||||||
<!-- {valves[property]} -->
|
<!-- {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">
|
<div class=" flex-1">
|
||||||
{#if valvesSpec.properties[property]?.enum ?? null}
|
{#if valvesSpec.properties[property]?.enum ?? null}
|
||||||
<select
|
<select
|
||||||
|
|
@ -92,6 +94,61 @@
|
||||||
dispatch('change');
|
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}
|
{:else}
|
||||||
<textarea
|
<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"
|
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
src/lib/components/common/Valves/MapSelector.svelte
Normal file
74
src/lib/components/common/Valves/MapSelector.svelte
Normal file
|
|
@ -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:
|
||||||
|
'© <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>
|
||||||
Loading…
Reference in a new issue