diff --git a/frontend/src/components/UnitPicker.vue b/frontend/src/components/UnitPicker.vue index 7f83820..6d70862 100644 --- a/frontend/src/components/UnitPicker.vue +++ b/frontend/src/components/UnitPicker.vue @@ -28,23 +28,40 @@ const freeName = ref(model.value?.kind === 'free' ? model.value.name : '') const onlyFree = computed(() => props.units.length === 0) const showFreeInput = computed(() => onlyFree.value || selected.value === FREE) -function emitChoice() { +/** The choice the two controls above currently stand for */ +const local = computed(() => { if (showFreeInput.value) { const name = freeName.value.trim() - model.value = name ? { kind: 'free', name } : undefined - return + // An empty box is an unfinished choice, not another association: the field + // reads as unfilled, while the box itself stays open to type in. + return name ? { kind: 'free', name } : undefined } const unit = props.units.find((u) => String(u.id) === selected.value) - model.value = unit ? { kind: 'known', id: unit.id, name: unit.name } : undefined + return unit ? { kind: 'known', id: unit.id, name: unit.name } : undefined +}) + +function sameChoice(left: UnitChoice | undefined, right: UnitChoice | undefined) { + if (!left || !right) return left === right + if (left.kind === 'known') return right.kind === 'known' && left.id === right.id + return right.kind === 'free' && left.name === right.name } -watch([selected, freeName], emitChoice) +watch(local, (choice) => { + model.value = choice +}) +/** + * Follows the model when it says something other than what the controls say — + * a form being reset, mostly. + * + * Comparing the two, rather than watching for any change, is what keeps an + * emptied text box open: clearing it does report "nothing chosen", and that + * report must not be read back as somebody else cancelling the choice. + */ watch(model, (choice) => { - if (choice === undefined && (selected.value || freeName.value)) { - selected.value = '' - freeName.value = '' - } + if (sameChoice(choice, local.value)) return + selected.value = choice?.kind === 'known' ? String(choice.id) : choice ? FREE : '' + freeName.value = choice?.kind === 'free' ? choice.name : '' }) diff --git a/frontend/src/components/admin/ReservationAdmin.vue b/frontend/src/components/admin/ReservationAdmin.vue index 08615a3..aa3bef3 100644 --- a/frontend/src/components/admin/ReservationAdmin.vue +++ b/frontend/src/components/admin/ReservationAdmin.vue @@ -39,19 +39,18 @@ const activeSorted = computed(() => byStart(props.active))