mirror of
https://github.com/enso-org/enso.git
synced 2025-01-03 09:03:57 +03:00
Support empty and non-numeric default values in number picker (#10251)
Numeric default (unchanged): <img width="400" alt="Screenshot 2024-06-11 at 10 19 21" src="https://github.com/enso-org/enso/assets/1047859/24549125-f7bd-4017-b04f-b9e8715203b1"> Non-numeric default: https://github.com/enso-org/enso/assets/1047859/bfe812e2-4d1c-477b-9500-f384fa499890 No default: <img width="400" alt="Screenshot 2024-06-11 at 10 19 36" src="https://github.com/enso-org/enso/assets/1047859/e99dcb0f-f146-4005-b4e5-79223fbe7744"> Closes #9449.
This commit is contained in:
parent
b053ab53e2
commit
904ffe1dd6
@ -7,18 +7,25 @@ import { computed, ref, type ComponentInstance } from 'vue'
|
||||
|
||||
const props = defineProps(widgetProps(widgetDefinition))
|
||||
const inputComponent = ref<ComponentInstance<typeof NumericInputWidget>>()
|
||||
const value = computed({
|
||||
get() {
|
||||
const valueStr = WidgetInput.valueRepr(props.input)
|
||||
return valueStr ? parseFloat(valueStr) : 0
|
||||
},
|
||||
set(value) {
|
||||
props.onUpdate({
|
||||
portUpdate: { value: value.toString(), origin: props.input.portId },
|
||||
})
|
||||
},
|
||||
|
||||
function setValue(value: number | string) {
|
||||
props.onUpdate({
|
||||
portUpdate: { value: value.toString(), origin: props.input.portId },
|
||||
})
|
||||
}
|
||||
|
||||
const value = computed<number | undefined>(() => {
|
||||
const inputValue = WidgetInput.valueRepr(props.input)
|
||||
if (inputValue == null) return undefined
|
||||
const inputNumber = parseFloat(inputValue)
|
||||
if (Number.isNaN(inputNumber)) return undefined
|
||||
return inputNumber
|
||||
})
|
||||
|
||||
const placeholder = computed<string | undefined>(() =>
|
||||
value.value == null ? WidgetInput.valueRepr(props.input) : undefined,
|
||||
)
|
||||
|
||||
const limits = computed(() => {
|
||||
const config = props.input.dynamicConfig
|
||||
if (config?.kind === 'Numeric_Input' && config?.minimum != null && config?.maximum != null) {
|
||||
@ -64,9 +71,11 @@ export const widgetDefinition = defineWidget(
|
||||
<template>
|
||||
<NumericInputWidget
|
||||
ref="inputComponent"
|
||||
v-model="value"
|
||||
class="WidgetNumber r-24"
|
||||
:limits="limits"
|
||||
:placeholder="placeholder"
|
||||
:modelValue="value"
|
||||
@update:modelValue="setValue"
|
||||
@click.stop
|
||||
@focus="editHandler.start()"
|
||||
@blur="editHandler.end()"
|
||||
|
@ -4,7 +4,10 @@ import { getTextWidthByFont } from '@/util/measurement'
|
||||
import { computed, ref, watch, type StyleValue } from 'vue'
|
||||
|
||||
const [model, modifiers] = defineModel<string>()
|
||||
const props = defineProps<{ autoSelect?: boolean }>()
|
||||
const props = defineProps<{
|
||||
autoSelect?: boolean
|
||||
placeholder?: string | undefined
|
||||
}>()
|
||||
const emit = defineEmits<{
|
||||
input: [value: string | undefined]
|
||||
change: [value: string | undefined]
|
||||
@ -35,7 +38,9 @@ const cssFont = computed(() => {
|
||||
const ADDED_WIDTH_PX = 2
|
||||
|
||||
const getTextWidth = (text: string) => getTextWidthByFont(text, cssFont.value)
|
||||
const inputWidth = computed(() => getTextWidth(`${innerModel.value}`) + ADDED_WIDTH_PX)
|
||||
const inputWidth = computed(
|
||||
() => getTextWidth(innerModel.value || (props.placeholder ?? '')) + ADDED_WIDTH_PX,
|
||||
)
|
||||
const inputStyle = computed<StyleValue>(() => ({ width: `${inputWidth.value}px` }))
|
||||
|
||||
function onEnterDown() {
|
||||
@ -59,7 +64,8 @@ defineExpose({
|
||||
<input
|
||||
ref="inputNode"
|
||||
v-model="innerModel"
|
||||
class="AutoSizedInput"
|
||||
class="AutoSizedInput input"
|
||||
:placeholder="placeholder ?? ''"
|
||||
:style="inputStyle"
|
||||
@pointerdown.stop
|
||||
@click.stop
|
||||
@ -93,8 +99,8 @@ defineExpose({
|
||||
}
|
||||
}
|
||||
|
||||
input::-webkit-outer-spin-button,
|
||||
input::-webkit-inner-spin-button {
|
||||
.input::-webkit-outer-spin-button,
|
||||
.input::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
@ -4,7 +4,8 @@ import { computed, ref, watch, type CSSProperties, type ComponentInstance } from
|
||||
import AutoSizedInput from './AutoSizedInput.vue'
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: number | string
|
||||
modelValue: number | undefined
|
||||
placeholder?: string | undefined
|
||||
limits?: { min: number; max: number } | undefined
|
||||
}>()
|
||||
const emit = defineEmits<{
|
||||
@ -14,16 +15,15 @@ const emit = defineEmits<{
|
||||
input: [content: string]
|
||||
}>()
|
||||
|
||||
const inputFieldActive = ref(false)
|
||||
// Edited value reflects the `modelValue`, but does not update it until the user defocuses the field.
|
||||
const editedValue = ref(`${props.modelValue}`)
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newValue) => {
|
||||
editedValue.value = `${newValue}`
|
||||
},
|
||||
)
|
||||
const DEFAULT_PLACEHOLDER = ''
|
||||
const SLIDER_INPUT_THRESHOLD = 4.0
|
||||
const MIN_CONTENT_WIDTH = 56
|
||||
|
||||
// Edited value reflects the `modelValue`, but does not update it until the user defocuses the field.
|
||||
const editedValue = ref('')
|
||||
const valueString = computed(() => (props.modelValue != null ? props.modelValue.toString() : ''))
|
||||
watch(valueString, (newValue) => (editedValue.value = newValue), { immediate: true })
|
||||
const inputFieldActive = ref(false)
|
||||
|
||||
let dragState: { thresholdMet: boolean } | undefined = undefined
|
||||
const dragPointer = usePointer(
|
||||
@ -68,7 +68,6 @@ const sliderWidth = computed(() => {
|
||||
})
|
||||
|
||||
const inputComponent = ref<ComponentInstance<typeof AutoSizedInput>>()
|
||||
const MIN_CONTENT_WIDTH = 56
|
||||
|
||||
const inputStyle = computed<CSSProperties>(() => {
|
||||
const value = `${editedValue.value}`
|
||||
@ -92,7 +91,7 @@ const inputStyle = computed<CSSProperties>(() => {
|
||||
})
|
||||
|
||||
function emitUpdate() {
|
||||
if (`${props.modelValue}` !== editedValue.value) {
|
||||
if (valueString.value !== editedValue.value) {
|
||||
emit('update:modelValue', editedValue.value)
|
||||
}
|
||||
}
|
||||
@ -110,7 +109,7 @@ function focused() {
|
||||
|
||||
defineExpose({
|
||||
cancel: () => {
|
||||
editedValue.value = `${props.modelValue}`
|
||||
editedValue.value = valueString.value
|
||||
inputComponent.value?.blur()
|
||||
},
|
||||
blur: () => inputComponent.value?.blur(),
|
||||
@ -126,6 +125,7 @@ defineExpose({
|
||||
class="NumericInputWidget"
|
||||
:class="{ slider: sliderWidth != null }"
|
||||
:style="{ ...inputStyle, '--slider-width': sliderWidth }"
|
||||
:placeholder="placeholder ?? DEFAULT_PLACEHOLDER"
|
||||
v-on="dragPointer.events"
|
||||
@click.stop
|
||||
@blur="blurred"
|
||||
|
Loading…
Reference in New Issue
Block a user