forked from jsnbuchanan/crowd-funder-for-time-pwa
Fix TypeScript any types and remove deprecated Dexie code
- Replace Record<string, any> with Record<string, string> for query params - Fix error handling from catch(err: any) to catch(err: unknown) - Add EntityData interface for proper entity typing - Update EntitySelectionEvent interface with union types - Remove USE_DEXIE_DB conditionals and unused import - Clean up database service calls removing Dexie fallbacks Resolves 9 TypeScript any type warnings, improves type safety across entity selection components, and removes deprecated database migration code.
This commit is contained in:
226
src/components/AmountInput.vue
Normal file
226
src/components/AmountInput.vue
Normal file
@@ -0,0 +1,226 @@
|
||||
/** * AmountInput.vue - Specialized amount input with increment/decrement
|
||||
controls * * Extracted from GiftedDialog.vue to handle numeric amount input *
|
||||
with increment/decrement buttons and validation. * * @author Matthew Raymer */
|
||||
<template>
|
||||
<div class="flex flex-grow">
|
||||
<button
|
||||
class="rounded-s border border-e-0 border-slate-400 bg-slate-200 px-4 py-2"
|
||||
:disabled="isAtMinimum"
|
||||
type="button"
|
||||
@click.prevent="decrement"
|
||||
>
|
||||
<font-awesome icon="chevron-left" />
|
||||
</button>
|
||||
|
||||
<input
|
||||
:id="inputId"
|
||||
v-model="displayValue"
|
||||
type="number"
|
||||
:min="min"
|
||||
:max="max"
|
||||
:step="step"
|
||||
class="flex-1 border border-e-0 border-slate-400 px-2 py-2 text-center w-[1px]"
|
||||
@input="handleInput"
|
||||
@blur="handleBlur"
|
||||
/>
|
||||
|
||||
<button
|
||||
class="rounded-e border border-slate-400 bg-slate-200 px-4 py-2"
|
||||
:disabled="isAtMaximum"
|
||||
type="button"
|
||||
@click.prevent="increment"
|
||||
>
|
||||
<font-awesome icon="chevron-right" />
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue, Watch, Emit } from "vue-facing-decorator";
|
||||
import { logger } from "@/utils/logger";
|
||||
|
||||
/**
|
||||
* AmountInput - Numeric input with increment/decrement controls
|
||||
*
|
||||
* Features:
|
||||
* - Increment/decrement buttons with validation
|
||||
* - Configurable min/max values and step size
|
||||
* - Input validation and formatting
|
||||
* - Disabled state handling for boundary values
|
||||
* - Emits update events for v-model compatibility
|
||||
*/
|
||||
@Component
|
||||
export default class AmountInput extends Vue {
|
||||
/** Current numeric value */
|
||||
@Prop({ required: true })
|
||||
value!: number;
|
||||
|
||||
/** Minimum allowed value */
|
||||
@Prop({ default: 0 })
|
||||
min!: number;
|
||||
|
||||
/** Maximum allowed value */
|
||||
@Prop({ default: Number.MAX_SAFE_INTEGER })
|
||||
max!: number;
|
||||
|
||||
/** Step size for increment/decrement */
|
||||
@Prop({ default: 1 })
|
||||
step!: number;
|
||||
|
||||
/** Input element ID for accessibility */
|
||||
@Prop({ default: "amount-input" })
|
||||
inputId!: string;
|
||||
|
||||
/** Internal display value for input field */
|
||||
private displayValue: string = "0";
|
||||
|
||||
/**
|
||||
* Initialize display value from prop
|
||||
*/
|
||||
mounted(): void {
|
||||
logger.debug("[AmountInput] mounted()", {
|
||||
value: this.value,
|
||||
min: this.min,
|
||||
max: this.max,
|
||||
step: this.step,
|
||||
});
|
||||
this.displayValue = this.value.toString();
|
||||
logger.debug("[AmountInput] mounted() - displayValue set", {
|
||||
displayValue: this.displayValue,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Watch for external value changes
|
||||
*/
|
||||
@Watch("value")
|
||||
onValueChange(newValue: number): void {
|
||||
this.displayValue = newValue.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if current value is at minimum
|
||||
*/
|
||||
get isAtMinimum(): boolean {
|
||||
const result = this.value <= this.min;
|
||||
logger.debug("[AmountInput] isAtMinimum", {
|
||||
value: this.value,
|
||||
min: this.min,
|
||||
result,
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if current value is at maximum
|
||||
*/
|
||||
get isAtMaximum(): boolean {
|
||||
const result = this.value >= this.max;
|
||||
logger.debug("[AmountInput] isAtMaximum", {
|
||||
value: this.value,
|
||||
max: this.max,
|
||||
result,
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment the value by step size
|
||||
*/
|
||||
increment(): void {
|
||||
logger.debug("[AmountInput] increment() called", {
|
||||
currentValue: this.value,
|
||||
step: this.step,
|
||||
});
|
||||
const newValue = Math.min(this.value + this.step, this.max);
|
||||
logger.debug("[AmountInput] increment() calculated newValue", {
|
||||
newValue,
|
||||
});
|
||||
this.updateValue(newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrement the value by step size
|
||||
*/
|
||||
decrement(): void {
|
||||
logger.debug("[AmountInput] decrement() called", {
|
||||
currentValue: this.value,
|
||||
step: this.step,
|
||||
});
|
||||
const newValue = Math.max(this.value - this.step, this.min);
|
||||
logger.debug("[AmountInput] decrement() calculated newValue", {
|
||||
newValue,
|
||||
});
|
||||
this.updateValue(newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle direct input changes
|
||||
*/
|
||||
handleInput(): void {
|
||||
const numericValue = parseFloat(this.displayValue);
|
||||
if (!isNaN(numericValue)) {
|
||||
const clampedValue = Math.max(this.min, Math.min(numericValue, this.max));
|
||||
this.updateValue(clampedValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle input blur - ensure display value matches actual value
|
||||
*/
|
||||
handleBlur(): void {
|
||||
this.displayValue = this.value.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the value and emit change event
|
||||
*/
|
||||
private updateValue(newValue: number): void {
|
||||
logger.debug("[AmountInput] updateValue() called", {
|
||||
oldValue: this.value,
|
||||
newValue,
|
||||
});
|
||||
if (newValue !== this.value) {
|
||||
logger.debug(
|
||||
"[AmountInput] updateValue() - values different, updating and emitting",
|
||||
);
|
||||
this.displayValue = newValue.toString();
|
||||
this.emitUpdateValue(newValue);
|
||||
} else {
|
||||
logger.debug(
|
||||
"[AmountInput] updateValue() - values same, skipping update",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit update:value event
|
||||
*/
|
||||
@Emit("update:value")
|
||||
emitUpdateValue(value: number): number {
|
||||
logger.debug("[AmountInput] emitUpdateValue() - emitting value", {
|
||||
value,
|
||||
});
|
||||
return value;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* Remove spinner arrows from number input */
|
||||
input[type="number"]::-webkit-outer-spin-button,
|
||||
input[type="number"]::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
input[type="number"] {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
|
||||
/* Disabled button styles */
|
||||
button:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user