mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-05-04 05:02:17 +00:00
* use step2 -> step bind on slider widget * fix: Use step2 instead of legacy step property in WidgetSlider The WidgetSlider was using the legacy `step` property (10x input spec value) instead of `step2` (correct input spec value). This caused input spec step values to appear 10x larger than intended. - Use `widget.options.step2` (correct input spec value) - Remove fallback to `widget.options.step` (legacy 10x value) - Both properties coexist, so step2 should always be preferred Fixes input spec step values not being respected in Vue node sliders. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
171 lines
4.8 KiB
Vue
171 lines
4.8 KiB
Vue
<template>
|
|
<WidgetLayoutField :widget="widget">
|
|
<div
|
|
:class="
|
|
cn(WidgetInputBaseClass, 'flex items-center gap-2 w-full pl-4 pr-2')
|
|
"
|
|
>
|
|
<Slider
|
|
v-model="localValue"
|
|
v-bind="filteredProps"
|
|
:disabled="readonly"
|
|
class="flex-grow text-xs"
|
|
@update:model-value="onChange"
|
|
/>
|
|
<InputText
|
|
v-model="inputDisplayValue"
|
|
:disabled="readonly"
|
|
type="number"
|
|
:min="widget.options?.min"
|
|
:max="widget.options?.max"
|
|
:step="stepValue"
|
|
class="w-[4em] text-center text-xs px-0 !border-none !shadow-none !bg-transparent"
|
|
size="small"
|
|
@blur="handleInputBlur"
|
|
@keydown="handleInputKeydown"
|
|
/>
|
|
</div>
|
|
</WidgetLayoutField>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import InputText from 'primevue/inputtext'
|
|
import Slider from 'primevue/slider'
|
|
import { computed, ref, watch } from 'vue'
|
|
|
|
import { useNumberWidgetValue } from '@/composables/graph/useWidgetValue'
|
|
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
|
|
import { cn } from '@/utils/tailwindUtil'
|
|
import {
|
|
STANDARD_EXCLUDED_PROPS,
|
|
filterWidgetProps
|
|
} from '@/utils/widgetPropFilter'
|
|
|
|
import { WidgetInputBaseClass } from './layout'
|
|
import WidgetLayoutField from './layout/WidgetLayoutField.vue'
|
|
|
|
const props = defineProps<{
|
|
widget: SimplifiedWidget<number>
|
|
modelValue: number
|
|
readonly?: boolean
|
|
}>()
|
|
|
|
const emit = defineEmits<{
|
|
'update:modelValue': [value: number]
|
|
}>()
|
|
|
|
// Use the composable for consistent widget value handling
|
|
const { localValue, onChange } = useNumberWidgetValue(
|
|
props.widget,
|
|
props.modelValue,
|
|
emit
|
|
)
|
|
|
|
const filteredProps = computed(() =>
|
|
filterWidgetProps(props.widget.options, STANDARD_EXCLUDED_PROPS)
|
|
)
|
|
|
|
// Get the precision value for proper number formatting
|
|
const precision = computed(() => {
|
|
const p = props.widget.options?.precision
|
|
// Treat negative or non-numeric precision as undefined
|
|
return typeof p === 'number' && p >= 0 ? p : undefined
|
|
})
|
|
|
|
// Calculate the step value based on precision or widget options
|
|
const stepValue = computed(() => {
|
|
// Use step2 (correct input spec value) instead of step (legacy 10x value)
|
|
if (props.widget.options?.step2 !== undefined) {
|
|
return String(props.widget.options.step2)
|
|
}
|
|
// Otherwise, derive from precision
|
|
if (precision.value !== undefined) {
|
|
if (precision.value === 0) {
|
|
return '1'
|
|
}
|
|
// For precision > 0, step = 1 / (10^precision)
|
|
// precision 1 → 0.1, precision 2 → 0.01, etc.
|
|
return (1 / Math.pow(10, precision.value)).toFixed(precision.value)
|
|
}
|
|
// Default to 'any' for unrestricted stepping
|
|
return 'any'
|
|
})
|
|
|
|
// Format a number according to the widget's precision
|
|
const formatNumber = (value: number): string => {
|
|
if (precision.value === undefined) {
|
|
// No precision specified, return as-is
|
|
return String(value)
|
|
}
|
|
// Use toFixed to ensure correct decimal places
|
|
return value.toFixed(precision.value)
|
|
}
|
|
|
|
// Apply precision-based rounding to a number
|
|
const applyPrecision = (value: number): number => {
|
|
if (precision.value === undefined) {
|
|
// No precision specified, return as-is
|
|
return value
|
|
}
|
|
if (precision.value === 0) {
|
|
// Integer precision
|
|
return Math.round(value)
|
|
}
|
|
// Round to the specified decimal places
|
|
const multiplier = Math.pow(10, precision.value)
|
|
return Math.round(value * multiplier) / multiplier
|
|
}
|
|
|
|
// Keep a separate display value for the input field
|
|
const inputDisplayValue = ref(formatNumber(localValue.value))
|
|
|
|
// Update display value when localValue changes from external sources
|
|
watch(localValue, (newValue) => {
|
|
inputDisplayValue.value = formatNumber(newValue)
|
|
})
|
|
|
|
const handleInputBlur = (event: Event) => {
|
|
const target = event.target as HTMLInputElement
|
|
const value = target.value || '0'
|
|
const parsed = parseFloat(value)
|
|
|
|
if (!isNaN(parsed)) {
|
|
// Apply precision-based rounding
|
|
const roundedValue = applyPrecision(parsed)
|
|
onChange(roundedValue)
|
|
// Update display value with proper formatting
|
|
inputDisplayValue.value = formatNumber(roundedValue)
|
|
}
|
|
}
|
|
|
|
const handleInputKeydown = (event: KeyboardEvent) => {
|
|
if (event.key === 'Enter') {
|
|
const target = event.target as HTMLInputElement
|
|
const value = target.value || '0'
|
|
const parsed = parseFloat(value)
|
|
|
|
if (!isNaN(parsed)) {
|
|
// Apply precision-based rounding
|
|
const roundedValue = applyPrecision(parsed)
|
|
onChange(roundedValue)
|
|
// Update display value with proper formatting
|
|
inputDisplayValue.value = formatNumber(roundedValue)
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
/* Remove number input spinners */
|
|
:deep(input[type='number']::-webkit-inner-spin-button),
|
|
:deep(input[type='number']::-webkit-outer-spin-button) {
|
|
-webkit-appearance: none;
|
|
margin: 0;
|
|
}
|
|
|
|
:deep(input[type='number']) {
|
|
-moz-appearance: textfield;
|
|
appearance: textfield;
|
|
}
|
|
</style>
|