2 puntos por GN⁺ 2024-12-06 | 1 comentarios | Compartir por WhatsApp
  • @stanko/dual-range-input crea un slider de rango dual combinando dos inputs range nativos de HTML, aprovechando tal cual la interacción y accesibilidad predeterminadas del navegador
  • La implementación se centra en unos 50 líneas de JavaScript para sincronizar los valores de entrada y CSS, por lo que es más simple que los enfoques tradicionales que vuelven a crear manualmente el arrastre y la accesibilidad
  • Los dos inputs actualizan su min, max y ancho según el punto medio de los valores seleccionados, y al hacer clic en la pista el thumb más cercano se mueve a ese valor
  • Como la pista del input range es más corta que el ancho real del input, se suma el ancho del thumb al cálculo y el padding del wrapper absorbe el ancho sobrante
  • El tema puede ajustarse con variables CSS y gradients, lo que permite sobrescribir el color del rango seleccionado, el tamaño del thumb, el color de la pista y más

Slider dual hecho con range input nativo

  • @stanko/dual-range-input implementa una entrada de rango dual colocando dos inputs range de HTML uno junto al otro
  • Aquí “nativo” no significa un único control del navegador, sino que usa dos inputs range para conservar la interacción predeterminada del navegador y las funciones de accesibilidad
  • La implementación consiste en unos 50 líneas de JavaScript que sincronizan los inputs y CSS para el acabado visual
  • El punto de partida fue la necesidad de una UI para ajustar el valor mínimo y máximo de parámetros en una herramienta de dibujo generativo
  • Había dos requisitos
    • usar inputs range de HTML nativos
    • al hacer clic en la pista, el thumb más cercano al punto pulsado debía moverse a ese valor

Sincronización de valores y manejo de clics

  • Cuando cambia uno de los dos inputs, la librería calcula el punto medio entre los dos valores seleccionados
    • configura los atributos min y max de cada input según ese punto medio
    • también actualiza el ancho de ambos inputs tomando como referencia el punto medio
  • Como los inputs se redimensionan para encontrarse en el punto medio, al hacer clic entre ambos thumbs se mueve el thumb más cercano a la posición del clic
    • si la cantidad de pasos entre ambos thumbs es impar, tiene prioridad el input usado más recientemente

Cálculos CSS y estilos

  • La pista del input range es más corta que el ancho completo del input, y el navegador deja espacio a ambos lados para que el thumb no sobresalga
    • si se divide el ancho del input simplemente en 25% y 75%, la proporción real de la pista no coincide
    • la implementación suma --dri-thumb-width al ancho de cada input y agrega padding al wrapper para alojar el ancho extra
  • El estilo usa las capacidades de CSS del navegador para personalizar los inputs range
    • da estilo a la pista y al thumb, y evita que aparezca border radius en la parte central donde se unen ambas pistas
  • El tema puede crearse con sobrescritura de variables CSS
    • entre las variables principales están --dri-height, --dri-thumb-width, --dri-thumb-color, --dri-track-color, --dri-track-filled-color, --dri-track-border-radius y otras
  • El rango seleccionado se pinta con un gradient de CSS
    • la posición del gradient se define con la variable --dri-gradient-position
    • el código actualiza también esta variable al modificar el ancho de los inputs
  • Esta implementación prioriza aprovechar elementos nativos siempre que sea posible, en lugar de reconstruir el arrastre y la accesibilidad con una librería personalizada

1 comentarios

 
GN⁺ 2024-12-06
Comentarios en Hacker News
  • Creo que este tipo de slider siempre debería permitir arrastrar también la zona central. O sea, si tomas el centro, ambos controles deberían moverse al mismo tiempo
    Por cierto, Unity lo maneja así: https://docs.unity3d.com/StaticFiles/ScriptRefImages/EditorG...

    • En cambio, yo probablemente ni habría pensado en hacer clic en la zona central. En mi cabeza esto no se veía como un widget completamente nuevo, sino como un slider modificado
      “Siempre” quizá sea una palabra demasiado fuerte
  • Soy el autor. Qué gusto ver esto en HN
    Vi que empezaron a dejar issues y reportes de bugs aquí, pero ya es tarde por este lado, así que lo revisaré mañana
    Lo hice porque lo necesitaba para trabajo con imágenes generativas, y prefiero ver cómo cambia la imagen mientras arrastro un slider en lugar de meter números
    Traté de mantenerlo lo más minimalista posible

    • Me gustó mucho este post. Lo leí con el café de la mañana y disfruté la explicación del proceso de cálculo y del modo de depuración; me pareció una forma muy ingeniosa de aprovechar componentes nativos
      Ahora mismo uso SwiftUI, donde casi es demasiado fácil crear tus propios componentes. Pero como explicó el autor, eso no siempre es lo mejor, y los componentes nativos muchas veces vienen bastante completos
      También probé el post en Firefox para iOS, que es un navegador medio peculiar, y funcionó bien. Está muy bien hecho
  • Me topé con un bug apenas al probarlo. Si arrastras el slider a 100 - 100 y luego intentas dejarlo en 99 - 99, no se puede
    Tampoco puedes llegar desde 99 - 100, ni desde 98 - 99. La única forma de llegar a 99 - 99 es bajar primero el límite inferior por debajo de 98
    Si desde 98 - 99 bajas a 97 - 99, entonces sí puedes llegar a 99 - 99
    Este bug puede pasar en cualquier parte; 99 - 99 solo fue el caso más fácil de reproducir por estar cerca del extremo del slider. En general, en cualquier estado X - X, si mueves un extremo un paso, no puedes arrastrar el otro extremo hacia ese hueco. Para cerrar esa distancia, antes tienes que alejar más el otro extremo

    • ¡Buen hallazgo! De hecho sí había pensado en ese caso, y funciona en Chrome y Firefox, pero parece que Safari no dispara el evento de focus en mousedown, sino hasta que ya empezó a mover el slider
      Gracias por el reporte, lo voy a corregir
    • En Chromium sobre Linux de escritorio, 100-100 → 99-100 → 99-99 sí funciona bien
      En cambio, hay otro bug visual. Si empiezas en 23-25 y arrastras a 24-25, el tirador derecho que está en 25 se mueve unos cuantos píxeles. Si luego regresas el tirador izquierdo a 23, el derecho también vuelve a su lugar
    • Por eso me gusta hacer herramientas para desarrolladores
  • ¡Está genial! Siempre he pensado que al input HTML le hace falta una implementación de rango de dos valores, porque tiene bastante sentido
    Por ejemplo, hay casos de uso como “quiero filtrar productos con al menos X widgets y como máximo Y widgets”
    Pero encontré un bug en Firefox que quería señalar. Cada vez que haces clic en el control mínimo o máximo, el valor cambia un paso hacia arriba o hacia abajo
    Si intentas hacer clic justo en el centro del tirador, el valor no cambia, pero si haces clic apenas a la izquierda, baja un paso, y si haces clic apenas a la derecha, sube
    Lo esperable sería que el valor solo cambie cuando hagas una acción explícita de cambio, es decir, mover realmente el puntero del mouse mientras mantienes presionado el botón

    • Sinceramente, no creo que eso sea un bug. Todos los ejemplos están bastante compactos y el tirador cubre varios pasos de la pista
      Firefox interpreta ese clic como si hubieras hecho clic directamente en la pista. Esta librería no interviene en eso; simplemente es el comportamiento de Firefox
  • El hecho de que funcione con teclado es excelente. Funciona correctamente y hasta puedes retroceder manteniendo presionado Shift-Tab
    Está realmente muy bien hecho, y aunque sea un detalle pequeño, se agradece que esa interacción esté implementada de forma correcta

  • En el sistema de diseño de nuestra empresa, un becario hizo una prueba de concepto parecida, colocando los controles uno al lado del otro, pero seguía apareciendo un efecto de saltos de píxeles en uno de los inputs y al final no logramos hacerlo bien
    Así que un segundo becario propuso la solución de usar 2 input range superpuestos

  • Cada vez que necesitaba un input de rango usaba el slider de jQuery UI, y tenía que meter jQuery y jQuery UI a la página solo por ese slider. Esto está muy bien
    Edit: ¿habría posibilidad de publicar también un archivo JS normal listo para usar, en vez de solo un módulo ESM?

  • No sé si se pueden reportar bugs aquí. En los dos primeros ejemplos, los controles interceptan la interacción táctil y bloquean el scroll. Los tres ejemplos posteriores no hacen eso
    Como efecto secundario, no puedes arrastrar hacia arriba para intentar ver la etiqueta debajo del dedo, y la página se desplaza haciendo que la etiqueta se quede debajo del dedo todo el tiempo

    • Si puedes reproducirlo, estaría bien que abras un issue en GitHub [1]
      Me da curiosidad qué navegador y sistema operativo estás usando. Como esta librería usa inputs nativos, no hace nada relacionado con bloquear el scroll
      En mi iPhone, todos los elementos input[type=range], con o sin estilos, bloquean el scroll
      [1] https://github.com/Stanko/dual-range-input/issues
  • Lo de “nativo” es debatible. La librería usa dos range inputs HTML nativos, así que dicen que se conservan las interacciones nativas y las funciones de accesibilidad, pero para mí el criterio es claro
    Si necesita JavaScript para funcionar como fue diseñado, entonces ya no es nativo

    • Estoy de acuerdo. Yo también diría que normalmente se usa así
      Aun así, entiendo lo que quería decir el autor. En vez de implementar a mano un montón de div movidos por JS, compuso sliders normales, así que se acerca mucho más a una solución nativa. Si no puedes reemplazar JavaScript con CSS ingenioso, probablemente sea lo más cercano posible
      Independientemente de si es nativo o no, este tipo de widget es muy útil cuando está disponible, y ese espacio ha tenido pocas implementaciones
    • Normalmente sí, pero tampoco es una división completamente nítida. Hay elementos que son nativos, pero necesitan JavaScript para abrirse de forma modal
      Antes del atributo popover, directamente no se podían abrir sin JavaScript
      Y también existen métodos nativos de JavaScript vanilla que vale la pena distinguir de librerías que ni siquiera usan las herramientas internas del navegador, como en la validación de formularios
  • Cuando hice https://spodj.intermer.net/, andaba buscando algo así
    Terminé usando React de forma medio improvisada para conseguir la funcionalidad que necesitaba, porque no soy muy bueno en frontend