<!--
  Canonical settings number field (DRY pass #161): a compact numeric v-text-field
  with a built-in clamp to [min,max] on commit. Hand-rolled identically across the
  ML settings cards (HeadsCard x6, CropProposersCard, VideoEmbeddingCard).

  The clamp is the point: the cards previously sent Number(raw) straight to the
  API, so an out-of-range value bounced off the API's 400 validator (only
  TranslationCard clamped). This is now the single home for that clamp.

  Binds `modelValue` (v-model) and emits `change` on blur/enter AFTER clamping, so
  the parent's save reads the already-clamped value — same as the prior
  `v-model.number` + `@change=save` pattern.
-->
<template>
  <v-text-field
    :model-value="modelValue"
    :label="label"
    type="number"
    :min="min"
    :max="max"
    :step="step"
    :disabled="disabled"
    :density="density" hide-details
    :style="{ maxWidth }"
    @update:model-value="v => emit('update:modelValue', v)"
    @change="onCommit"
  />
</template>

<script setup>
const props = defineProps({
  modelValue: { type: [Number, String], default: null },
  label: { type: String, default: '' },
  min: { type: [Number, String], default: null },
  max: { type: [Number, String], default: null },
  step: { type: [Number, String], default: 1 },
  maxWidth: { type: String, default: '200px' },
  density: { type: String, default: 'compact' },
  disabled: { type: Boolean, default: false },
})
const emit = defineEmits(['update:modelValue', 'change'])

function onCommit() {
  // On blur/enter: coerce to a number and clamp to [min,max] so an out-of-range
  // value never reaches the API. props.modelValue reflects the latest keystroke
  // (kept in sync by the passthrough above); re-emit the clamped number, then let
  // the parent persist.
  let n = Number(props.modelValue)
  if (!Number.isNaN(n)) {
    if (props.min !== null && props.min !== '') n = Math.max(Number(props.min), n)
    if (props.max !== null && props.max !== '') n = Math.min(Number(props.max), n)
    if (n !== Number(props.modelValue)) emit('update:modelValue', n)
  }
  emit('change')
}
</script>
