Switch.jsx 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
  1. import { h } from 'preact';
  2. import { useCallback, useState } from 'preact/hooks';
  3. export default function Switch({ checked, id, onChange, label, labelPosition = 'before' }) {
  4. const [isFocused, setFocused] = useState(false);
  5. const handleChange = useCallback(
  6. (event) => {
  7. if (onChange) {
  8. onChange(id, !checked);
  9. }
  10. },
  11. [id, onChange, checked]
  12. );
  13. const handleFocus = useCallback(() => {
  14. onChange && setFocused(true);
  15. }, [onChange, setFocused]);
  16. const handleBlur = useCallback(() => {
  17. onChange && setFocused(false);
  18. }, [onChange, setFocused]);
  19. return (
  20. <label
  21. htmlFor={id}
  22. className={`flex items-center space-x-4 w-full ${onChange ? 'cursor-pointer' : 'cursor-not-allowed'}`}
  23. >
  24. {label && labelPosition === 'before' ? (
  25. <div data-testid={`${id}-label`} className="inline-flex flex-grow">
  26. {label}
  27. </div>
  28. ) : null}
  29. <div
  30. onMouseOver={handleFocus}
  31. onMouseOut={handleBlur}
  32. className={`self-end w-8 h-5 relative ${!onChange ? 'opacity-60' : ''}`}
  33. >
  34. <div className="relative overflow-hidden">
  35. <input
  36. data-testid={`${id}-input`}
  37. className="absolute left-48"
  38. onBlur={handleBlur}
  39. onFocus={handleFocus}
  40. tabIndex="0"
  41. id={id}
  42. type="checkbox"
  43. onChange={handleChange}
  44. checked={checked}
  45. />
  46. </div>
  47. <div
  48. className={`w-8 h-3 absolute top-1 left-1 ${
  49. !checked ? 'bg-gray-300' : 'bg-blue-300'
  50. } rounded-full shadow-inner`}
  51. />
  52. <div
  53. className={`transition-all absolute w-5 h-5 rounded-full shadow-md inset-y-0 left-0 ring-opacity-30 ${
  54. isFocused ? 'ring-4 ring-gray-500' : ''
  55. } ${checked ? 'bg-blue-600' : 'bg-white'} ${isFocused && checked ? 'ring-blue-500' : ''}`}
  56. style={checked ? 'transform: translateX(100%);' : 'transform: translateX(0%);'}
  57. />
  58. </div>
  59. {label && labelPosition !== 'before' ? (
  60. <div data-testid={`${id}-label`} class="inline-flex flex-grow">
  61. {label}
  62. </div>
  63. ) : null}
  64. </label>
  65. );
  66. }