Búsqueda de sitios web

javascript - el evento onDrag clientX devuelve 0 en Firefox al convertir el componente a Typescript


Estoy convirtiendo un componente personalizado de deslizante de React JS a un componente de React TypeScript (jsx) y me encuentro con el problema de que deja de deslizar en Firefox. e.clientX siempre es 0, pero el componente de React JS normal funciona como se espera en Firefox. Ambos funcionan bien en Chrome.

No estoy seguro de si esto todavía es el conocido error de Firefox respecto al evento "drag". He leído estas fuentes: 1, 2, 3, 4, 5.

Preguntas:

  1. ¿Sigue siendo este el mismo error mencionado en algunas de las fuentes mencionadas?
  2. ¿Por qué esto ocurre específicamente en TypeScript, y no en JavaScript?
  3. ¿Qué solución sugerirías para compatibilidad completa con los navegadores (Firefox, Chrome, Safari)?

Versión de Javascript

import {useRef, useState} from "react";

export default function Slider({initialValue, onChange}) {
    const sliderEl = useRef(null)
    const [dragging, setDragging] = useState(false)
    const [value, setValue] = useState(initialValue)

    const calculateValue = (x) => {
        const {x: sliderX, width: sliderWidth} = sliderEl.current.getBoundingClientRect()
        const value = (x - sliderX) / sliderWidth

        return Math.max(0, Math.min(value, 1))
    }

    const handleDrag = (e) => {
        if (e.clientX === 0) {
            return
        }

        setValue(calculateValue(e.clientX))
    }

    const handleClick = (e) => {
        const value = calculateValue(e.clientX)
        setValue(value)
        onChange(value)
    }

    const offsetLeft = `calc(${value * 100}% - 0.5rem)`

    return <div className="relative h-4" onMouseDown={handleClick} ref={sliderEl}>
        <div className="bg-primary-5 rounded-full h-1 w-full top-1.5 absolute"/>
        <div className="bg-primary-500 rounded-l-full h-1 top-1.5 absolute" style={{width: `${value * 100}%`}}/>
        <div className="rounded-full bg-primary-700 w-4 h-4 absolute top-0" style={{left: offsetLeft}}/>
        <div className="rounded-full bg-primary-500 w-4 h-4 absolute top-0 cursor-pointer"
            draggable={true}
            style={{left: offsetLeft, opacity: dragging ? 0 : 1}}
            onDragStart={(e) => { setDragging(true) }}
            onDrag={handleDrag}
            onDragEnd={(e) => {
                setDragging(false)
                onChange && onChange(value)
            }}
    />
    </div>
}

Conversión de Typescript

import React, { useRef, useState } from 'react'

type SliderProps = {
    initialValue: number,
    onChange: (value: number) => void
}

export default function Slider({ initialValue = 0, onChange }: SliderProps) {
    const sliderEl = useRef<HTMLDivElement>(null)
    const [dragging, setDragging] = useState(false)
    const [value, setValue] = useState<number>(initialValue)

    const calculateValue = (x: number): number => {
        if(!sliderEl.current) return 0
        const { x: sliderX, width: sliderWidth }: DOMRect = sliderEl.current.getBoundingClientRect()
        const value = (x - sliderX) / sliderWidth

        return Math.max(0, Math.min(value, 1))
    }

    const handleDrag = (e: React.MouseEvent<HTMLDivElement>) => {
        if (e.clientX === 0) {
            return
        }
        
        setValue(calculateValue(e.clientX))
    }

    const handleClick = (e: React.MouseEvent<HTMLDivElement>) => {
        const value = calculateValue(e.clientX)

        if(value) {
            setValue(value)
            onChange(value)
        }
    }

    const offsetLeft = `calc(${value * 100}% - 0.5rem)`

    return <div className="relative h-4" onMouseDown={handleClick} ref={sliderEl}>
        <div className="bg-primary-5 rounded-full h-1 w-full top-1.5 absolute"/>
        <div className="bg-primary-500 rounded-l-full h-1 top-1.5 absolute" style={{width: `${value * 100}%`}}/>
        <div className="rounded-full bg-primary-700 w-4 h-4 absolute top-0" style={{left: offsetLeft}}/>
        <div className="rounded-full bg-primary-500 w-4 h-4 absolute top-0 cursor-pointer"
            draggable={true}
            style={{left: offsetLeft, opacity: dragging ? 0 : 1}}
            onDragStart={(e) => { setDragging(true) }}
            onDrag={handleDrag}
            onDragEnd={(e) => {
                setDragging(false)
                onChange && onChange(value)
            }}
    />
    </div>
}

import React, { useRef, useState } from 'react'

type SliderProps = {
    initialValue: number,
    onChange: (value: number) => void
}

export default function Slider({ initialValue = 0, onChange }: SliderProps) {
    const sliderEl = useRef<HTMLDivElement>(null)
    const [dragging, setDragging] = useState(false)
    const [value, setValue] = useState<number>(initialValue)

    const calculateValue = (x: number): number => {
        if(!sliderEl.current) return 0
        const { x: sliderX, width: sliderWidth }: DOMRect = sliderEl.current.getBoundingClientRect()
        const value = (x - sliderX) / sliderWidth

        return Math.max(0, Math.min(value, 1))
    }

    const handleDrag = (e: React.MouseEvent<HTMLDivElement>) => {
        e.preventDefault()
        console.log(e.clientX)
        if (e.clientX === 0) {
            return
        }
        
        setValue(calculateValue(e.clientX))
    }

    const handleClick = (e: React.MouseEvent<HTMLDivElement>) => {
        const value = calculateValue(e.clientX)

        if(value) {
            setValue(value)
            onChange(value)
        }
    }

    const offsetLeft = `calc(${value * 100}% - 0.5rem)`

    return <div className="relative h-4" onMouseDown={handleClick} ref={sliderEl}>
        <div className="bg-primary-5 rounded-full h-1 w-full top-1.5 absolute"/>
        <div className="bg-primary-500 rounded-l-full h-1 top-1.5 absolute" style={{width: `${value * 100}%`}}/>
        <div className="rounded-full bg-primary-700 w-4 h-4 absolute top-0" style={{left: offsetLeft}}/>
        <div className="rounded-full bg-primary-500 w-4 h-4 absolute top-0 cursor-pointer"
            draggable={true}
            style={{left: offsetLeft, opacity: dragging ? 0 : 1}}
            onDragStart={(e) => { setDragging(true) }}
            onDrag={handleDrag}
            onDragEnd={(e) => {
                setDragging(false)
                onChange && onChange(value)
            }}
    />
    </div>
}

¡Gracias!