valueSlider.tsx 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. 'use client'
  2. import { useState } from "react"
  3. import { Slider, Tooltip } from "@heroui/react";
  4. import { InformationCircleIcon } from "@heroicons/react/24/outline";
  5. export function ValueSlider(
  6. props: {
  7. label: string,
  8. description: string,
  9. maxValue: number,
  10. minValue: number,
  11. step: number,
  12. defaultValue: number,
  13. onChange: (value: number) => void
  14. }
  15. ) {
  16. const [value, setValue] = useState(props.defaultValue);
  17. const [inputValue, setInputValue] = useState(props.defaultValue.toString());
  18. const handleChange = (value: number | number[]) => {
  19. if (isNaN(Number(value))) return;
  20. setValue(value as number);
  21. setInputValue(value.toString());
  22. props.onChange(value as number);
  23. };
  24. const contentRender = () => {
  25. return (
  26. <span>{props.description}</span>
  27. )
  28. }
  29. return (
  30. <Slider
  31. className="max-w-md p-1"
  32. classNames={{
  33. base: "max-w-md",
  34. label: "text-medium",
  35. }}
  36. name={props.label}
  37. label={props.label}
  38. key={props.label}
  39. maxValue={props.maxValue}
  40. minValue={props.minValue}
  41. renderLabel={({ children, ...props }) => (
  42. <label {...props} className="text-medium flex gap-2 items-center">
  43. {children}
  44. <Tooltip
  45. className="px-1.5 text-tiny text-default-600 rounded-small"
  46. content={contentRender()}
  47. placement="right"
  48. >
  49. <span className="transition-opacity opacity-80 hover:opacity-100">
  50. <InformationCircleIcon className="size-5" />
  51. </span>
  52. </Tooltip>
  53. </label>
  54. )}
  55. // eslint-disable-next-line @typescript-eslint/no-unused-vars
  56. renderValue={({ children, ...props }) => (
  57. <output {...props}>
  58. <Tooltip
  59. className="text-tiny text-default-500 rounded-md"
  60. content="Press Enter to confirm"
  61. placement="left"
  62. >
  63. <input
  64. aria-label="Input value"
  65. className="px-1 py-0.5 w-12 text-right text-small text-default-700 font-medium bg-default-100 outline-none transition-colors rounded-small border-medium border-transparent hover:border-primary focus:border-primary"
  66. type="text"
  67. value={inputValue}
  68. onChange={(e) => {
  69. const v = e.target.value;
  70. setInputValue(v);
  71. }}
  72. onKeyDown={(e) => {
  73. if (e.key === "Enter" && !isNaN(Number(inputValue))) {
  74. handleChange(Number(inputValue));
  75. }
  76. }}
  77. />
  78. </Tooltip>
  79. </output>
  80. )}
  81. // we extract the default children to render the input
  82. step={props.step}
  83. value={value}
  84. onChange={handleChange}
  85. />
  86. );
  87. }