RoleImpersonationRadio.tsx 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. import { Check, Minus } from 'lucide-react'
  2. import { cn } from 'ui'
  3. export interface RoleImpersonationRadioProps<T extends string> {
  4. label?: string
  5. description?: string
  6. value: T
  7. isSelected: boolean | 'partially'
  8. onSelectedChange: (value: T) => void
  9. icon?: React.ReactNode
  10. fullWidth?: boolean
  11. }
  12. export function RoleImpersonationRadio<T extends string>({
  13. label,
  14. description,
  15. value,
  16. isSelected,
  17. onSelectedChange,
  18. icon,
  19. fullWidth = false,
  20. }: RoleImpersonationRadioProps<T>) {
  21. return (
  22. <label
  23. className={cn(
  24. 'border border-default rounded-md bg-surface-200 hover:bg-overlay-hover hover:border-control px-4 py-3 cursor-pointer transition-colors',
  25. fullWidth ? 'w-full' : 'w-44',
  26. isSelected && 'border-foreground-muted hover:border-foreground-muted bg-surface-300'
  27. )}
  28. tabIndex={0}
  29. onKeyDown={(e) => {
  30. if (e.key === 'Enter' || e.key === ' ') {
  31. onSelectedChange(value)
  32. }
  33. }}
  34. htmlFor={`role-${value}`}
  35. >
  36. <div className="flex justify-between items-center mb-2">
  37. {icon && <div>{icon}</div>}
  38. {isSelected && (
  39. <div className="flex items-center justify-center p-0.5 bg-foreground text-background rounded-full">
  40. {typeof isSelected === 'boolean' && (
  41. <Check size={12} strokeWidth="4" className="text-background" />
  42. )}
  43. {isSelected === 'partially' && (
  44. <Minus size={12} strokeWidth="4" className="text-background" />
  45. )}
  46. </div>
  47. )}
  48. </div>
  49. <input
  50. id={`role-${value}`}
  51. type="radio"
  52. name="role"
  53. value={value}
  54. checked={Boolean(isSelected)}
  55. onChange={(e) => {
  56. onSelectedChange(e.target.value as T)
  57. }}
  58. className="invisible h-0 w-0 border-0"
  59. />
  60. <span
  61. className={cn(
  62. 'text-sm text-foreground-light whitespace-nowrap select-none transition-colors',
  63. isSelected && 'text-foreground'
  64. )}
  65. >
  66. {label ?? value}
  67. </span>
  68. {description && <p className="text-foreground-lighter text-xs">{description}</p>}
  69. </label>
  70. )
  71. }