fix : composant nombre et taux horaire

This commit is contained in:
2026-03-19 09:21:29 +01:00
parent 11d60e687b
commit c82eaa0939
8 changed files with 114 additions and 21 deletions

View File

@@ -26,11 +26,11 @@ const mountInputNumber = (props: InputNumberProps = {}) =>
})
describe('MalioInputNumber', () => {
it('renders the input with a fixed 20px height', () => {
it('renders the input with a fixed 22px height', () => {
const wrapper = mountInputNumber()
const input = wrapper.get('input')
expect(input.classes()).toContain('h-[20px]')
expect(input.classes()).toContain('h-[22px]')
})
it('renders the increment and decrement buttons with a fixed 20px height', () => {

View File

@@ -58,7 +58,7 @@
: hasSuccess
? 'text-m-success'
: 'text-m-muted',
'mt-1 text-xs ml-[2px] ',
'text-xs ml-[2px] ',
]"
>
{{ hint || error || success }}
@@ -159,7 +159,6 @@ const inputWidthStyle = computed(() => ({
maxWidth: '100%',
}))
const isMinusDisabled = computed(() =>
props.disabled || currentNumericValue.value <= minValue.value,
)
@@ -168,15 +167,17 @@ const isPlusDisabled = computed(() =>
props.disabled || currentNumericValue.value >= maxValue.value,
)
const mergedGroupClass = computed(() =>
twMerge(
'relative mt-4 flex h-12 w-full items-center',
props.groupClass,
),
)
const mergedInputClass = computed(() =>
twMerge(
' peer h-[20px] min-w-0 border bg-white text-center outline-none placeholder:text-transparent text-lg border-x-0 border-black',
' peer h-[22px] min-w-0 border bg-white text-center outline-none placeholder:text-transparent text-lg border-x-0 border-black',
props.disabled ? 'cursor-not-allowed text-black/60' : 'cursor-text',
hasError.value
? 'border-m-error focus:border-m-error [&:not(:placeholder-shown)]:border-m-error'
@@ -186,9 +187,10 @@ const mergedInputClass = computed(() =>
props.inputClass,
),
)
const mergedLabelClass = computed(() =>
twMerge(
'radio-text mt-px cursor-pointer text-black mr-3',
'cursor-pointer text-black mr-4 text-[18px]',
hasError.value ? 'text-m-error' : '',
hasSuccess.value ? 'text-m-success' : '',
props.disabled ? 'cursor-not-allowed text-black/60' : '',
@@ -198,7 +200,7 @@ const mergedLabelClass = computed(() =>
const mergedButtonMinusClass = computed(() =>
twMerge(
'h-[20px] w-[30px] border border-black rounded-s-[3px]',
'h-[22px] w-[40px] border border-black rounded-s-[3px]',
isMinusDisabled.value ? 'cursor-not-allowed text-black/60' : 'cursor-pointer',
hasError.value
? 'border-m-error'
@@ -207,9 +209,10 @@ const mergedButtonMinusClass = computed(() =>
: '',
),
)
const mergedButtonPlusClass = computed(() =>
twMerge(
'h-[20px] w-[30px] border border-black rounded-e-[3px]',
'h-[22px] w-[40px] border border-black rounded-e-[3px]',
isPlusDisabled.value ? 'cursor-not-allowed text-black/60' : 'cursor-pointer',
hasError.value
? 'border-m-error'

View File

@@ -31,9 +31,10 @@
@blur="onHoursBlur"
>
<span class="text-lg text-black">:</span>
<span class="text-[18px] text-black">:</span>
<input
ref="minutesInputRef"
:id="minutesInputId"
:name="minutesName"
autocomplete="off"
@@ -74,7 +75,7 @@
</template>
<script setup lang="ts">
import {computed, ref, useAttrs, useId, watch} from 'vue'
import {computed, nextTick, ref, useAttrs, useId, watch} from 'vue'
import {twMerge} from 'tailwind-merge'
defineOptions({name: 'MalioTime', inheritAttrs: false})
@@ -117,6 +118,7 @@ const generatedId = useId()
const hoursValue = ref('')
const minutesValue = ref('')
const activeField = ref<'hours' | 'minutes' | null>(null)
const minutesInputRef = ref<HTMLInputElement | null>(null)
const inputId = computed(() => props.id?.toString() || `malio-time-${generatedId}`)
const hoursInputId = computed(() => `${inputId.value}-hours`)
@@ -138,13 +140,15 @@ const sanitizeDigits = (value: string) =>
const normalizeHours = (value: string) => {
const digits = sanitizeDigits(value)
if (digits === '') return ''
return String(Math.min(Number.parseInt(digits, 10), 23))
if (Number.parseInt(digits, 10) > 23) return '23'
return digits
}
const normalizeMinutes = (value: string) => {
const digits = sanitizeDigits(value)
if (digits === '') return ''
return String(Math.min(Number.parseInt(digits, 10), 59))
if (Number.parseInt(digits, 10) > 59) return '59'
return digits
}
const parseTimeValue = (value: string | null | undefined) => {
@@ -159,6 +163,7 @@ const parseTimeValue = (value: string | null | undefined) => {
}
const syncFromModelValue = (value: string | null | undefined) => {
if (activeField.value) return
const parsedValue = parseTimeValue(value)
hoursValue.value = parsedValue.hours
minutesValue.value = parsedValue.minutes
@@ -179,7 +184,7 @@ const mergedGroupClass = computed(() =>
const mergedLabelClass = computed(() =>
twMerge(
'radio-text mt-px mr-3 cursor-pointer text-black',
'mt-px mr-4 cursor-pointer text-black text-[18px]',
hasError.value ? 'text-m-error' : '',
hasSuccess.value ? 'text-m-success' : '',
props.disabled ? 'cursor-not-allowed text-black/60' : '',
@@ -189,7 +194,7 @@ const mergedLabelClass = computed(() =>
const mergedInputClass = (field: 'hours' | 'minutes') =>
twMerge(
'h-7 w-9 border bg-white text-center text-lg outline-none rounded-md placeholder:text-m-muted',
'h-[30px] w-10 border bg-white text-center text-[18px] outline-none rounded-md placeholder:text-m-muted',
props.disabled ? 'cursor-not-allowed text-black/60 border-m-muted' : 'cursor-text',
hasError.value
? 'focus:border-2 border-m-error focus:border-m-error'
@@ -201,16 +206,16 @@ const mergedInputClass = (field: 'hours' | 'minutes') =>
props.inputClass,
)
const emitCurrentValue = () => {
const formattedHours = hoursValue.value ? padSegment(hoursValue.value) : '00'
const formattedMinutes = minutesValue.value ? padSegment(minutesValue.value) : '00'
const emitCurrentValue = (pad = false) => {
if (!hoursValue.value && !minutesValue.value) {
emit('update:modelValue', '')
return
}
emit('update:modelValue', `${formattedHours}:${formattedMinutes}`)
const h = pad ? padSegment(hoursValue.value || '0') : (hoursValue.value || '00')
const m = pad ? padSegment(minutesValue.value || '0') : (minutesValue.value || '00')
emit('update:modelValue', `${h}:${m}`)
}
const onHoursInput = (event: Event) => {
@@ -220,6 +225,10 @@ const onHoursInput = (event: Event) => {
hoursValue.value = normalizedValue
target.value = normalizedValue
emitCurrentValue()
if (normalizedValue.length === 2) {
nextTick(() => minutesInputRef.value?.focus())
}
}
const onMinutesInput = (event: Event) => {
@@ -240,7 +249,7 @@ const formatFieldOnBlur = (field: 'hours' | 'minutes') => {
minutesValue.value = padSegment(minutesValue.value)
}
emitCurrentValue()
emitCurrentValue(true)
}
const onHoursBlur = () => {