mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-08-23 09:13:43 +08:00
Moar Toggles
This commit is contained in:
@@ -6,6 +6,7 @@ import { html, reactive } from "https://esm.sh/@arrow-js/core"
|
||||
const state = reactive({
|
||||
layout: [],
|
||||
allKeys: [],
|
||||
paramMetaByKey: {},
|
||||
values: {},
|
||||
loadingLayout: true,
|
||||
loadingValues: true,
|
||||
@@ -34,12 +35,15 @@ async function fetchLayoutAndParams() {
|
||||
|
||||
// Extract flatter key map
|
||||
const keys = []
|
||||
const paramMetaByKey = {}
|
||||
for (const section of layoutData) {
|
||||
for (const p of section.params) {
|
||||
keys.push(p.key)
|
||||
paramMetaByKey[p.key] = p
|
||||
}
|
||||
}
|
||||
state.allKeys = keys
|
||||
state.paramMetaByKey = paramMetaByKey
|
||||
} catch (e) {
|
||||
console.error("Failed to fetch UI layout:", e)
|
||||
}
|
||||
@@ -58,6 +62,17 @@ async function fetchLayoutAndParams() {
|
||||
}
|
||||
|
||||
function syncInputs() {
|
||||
const selectValue = (value) => (value === null || value === undefined ? "" : String(value))
|
||||
const applySelectOptions = (el, options) => {
|
||||
el.innerHTML = ""
|
||||
for (const opt of options) {
|
||||
const o = document.createElement("option")
|
||||
o.value = String(opt.value)
|
||||
o.textContent = opt.label
|
||||
el.appendChild(o)
|
||||
}
|
||||
}
|
||||
|
||||
for (const key of state.allKeys) {
|
||||
const el = document.getElementById(`ds-${key}`)
|
||||
if (el) {
|
||||
@@ -65,20 +80,19 @@ function syncInputs() {
|
||||
el.checked = !!state.values[key]
|
||||
} else if (el.tagName === "SELECT") {
|
||||
const endpoint = el.getAttribute("data-endpoint")
|
||||
const inlineOptions = state.paramMetaByKey[key]?.options
|
||||
if (endpoint && !el.dataset.hydrated) {
|
||||
el.dataset.hydrated = "1"
|
||||
fetch(endpoint).then(r => r.json()).then(options => {
|
||||
el.innerHTML = ""
|
||||
for (const opt of options) {
|
||||
const o = document.createElement("option")
|
||||
o.value = opt.value
|
||||
o.textContent = opt.label
|
||||
el.appendChild(o)
|
||||
}
|
||||
el.value = state.values[key] || ""
|
||||
applySelectOptions(el, options)
|
||||
el.value = selectValue(state.values[key])
|
||||
}).catch(() => { el.innerHTML = '<option value="">Error loading</option>' })
|
||||
} else if (Array.isArray(inlineOptions) && inlineOptions.length > 0 && !el.dataset.hydrated) {
|
||||
el.dataset.hydrated = "1"
|
||||
applySelectOptions(el, inlineOptions)
|
||||
el.value = selectValue(state.values[key])
|
||||
} else {
|
||||
el.value = state.values[key] || ""
|
||||
el.value = selectValue(state.values[key])
|
||||
}
|
||||
} else {
|
||||
el.value = state.values[key]
|
||||
@@ -119,6 +133,41 @@ function formatSliderValue(val, stepStr, precisionInt, key) {
|
||||
return Number(v.toFixed(dec)).toString()
|
||||
}
|
||||
|
||||
function numericBounds(param) {
|
||||
const defaultBounds = {
|
||||
min: param.min !== undefined ? param.min : (param.data_type === "float" ? 0.0 : 0),
|
||||
max: param.max !== undefined ? param.max : (param.data_type === "float" ? 100.0 : 100),
|
||||
step: param.step !== undefined ? param.step : (param.data_type === "float" ? 0.01 : 1),
|
||||
}
|
||||
|
||||
const toFinite = (value) => {
|
||||
const n = Number(value)
|
||||
return Number.isFinite(n) ? n : null
|
||||
}
|
||||
|
||||
if (param.key === "ScreenBrightness") {
|
||||
return { min: 1, max: 101, step: 1 }
|
||||
}
|
||||
if (param.key === "ScreenBrightnessOnroad") {
|
||||
return { min: 0, max: 101, step: 1 }
|
||||
}
|
||||
|
||||
if (param.key === "SteerKP") {
|
||||
const base = toFinite(state.values.SteerKPStock) || toFinite(state.values.SteerKP) || 0.6
|
||||
return { min: +(base * 0.5).toFixed(2), max: +(base * 1.5).toFixed(2), step: 0.01 }
|
||||
}
|
||||
if (param.key === "SteerLatAccel") {
|
||||
const base = toFinite(state.values.SteerLatAccelStock) || toFinite(state.values.SteerLatAccel) || 2.0
|
||||
return { min: +(base * 0.5).toFixed(2), max: +(base * 1.25).toFixed(2), step: 0.01 }
|
||||
}
|
||||
if (param.key === "SteerRatio") {
|
||||
const base = toFinite(state.values.SteerRatioStock) || toFinite(state.values.SteerRatio) || 15.0
|
||||
return { min: +(base * 0.25).toFixed(2), max: +(base * 1.5).toFixed(2), step: 0.01 }
|
||||
}
|
||||
|
||||
return defaultBounds
|
||||
}
|
||||
|
||||
async function updateParam(key, elType) {
|
||||
const current = state.values[key]
|
||||
|
||||
@@ -161,7 +210,7 @@ function revertInput(key, current, elType) {
|
||||
const el = document.getElementById(`ds-${key}`)
|
||||
if (el) {
|
||||
if (elType === "checkbox") el.checked = !!current
|
||||
else if (elType === "dropdown") el.value = current || ""
|
||||
else if (elType === "dropdown") el.value = (current === null || current === undefined ? "" : String(current))
|
||||
else {
|
||||
el.value = current
|
||||
const displayEl = document.getElementById(`ds-display-${key}`)
|
||||
@@ -255,7 +304,7 @@ export function DeviceSettings() {
|
||||
if (!state.expanded[p.parent_key]) return ""
|
||||
}
|
||||
|
||||
const isNumeric = p.ui_type === "numeric" || p.data_type === "float" || p.data_type === "int"
|
||||
const isNumeric = p.ui_type === "numeric"
|
||||
const isChild = p.parent_key ? "ds-child-modifier" : ""
|
||||
|
||||
return html`
|
||||
@@ -276,18 +325,23 @@ export function DeviceSettings() {
|
||||
|
||||
${isNumeric ? html`
|
||||
<div class="ds-slider-container">
|
||||
${(() => {
|
||||
const bounds = numericBounds(p)
|
||||
return html`
|
||||
<input
|
||||
type="range"
|
||||
class="ds-slider"
|
||||
id="ds-${p.key}"
|
||||
min="${p.min !== undefined ? p.min : (p.data_type === 'float' ? 0.0 : 0)}"
|
||||
max="${p.max !== undefined ? p.max : (p.data_type === 'float' ? 100.0 : 100)}"
|
||||
step="${p.step !== undefined ? p.step : (p.data_type === 'float' ? 0.01 : 1)}"
|
||||
min="${bounds.min}"
|
||||
max="${bounds.max}"
|
||||
step="${bounds.step}"
|
||||
data-precision="${p.precision !== undefined ? p.precision : ''}"
|
||||
value="${state.values[p.key] !== undefined ? state.values[p.key] : ''}"
|
||||
@input="${(e) => handleSliderInput(e, p.key)}"
|
||||
@change="${() => updateParam(p.key, 'numeric')}"
|
||||
/>
|
||||
`
|
||||
})()}
|
||||
</div>
|
||||
` : p.ui_type === "dropdown" ? html`
|
||||
<select class="ds-select" id="ds-${p.key}"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user