You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

RadioButton.tsx 4.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2024 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. import styled from '@emotion/styled';
  21. import classNames from 'classnames';
  22. import React from 'react';
  23. import tw from 'twin.macro';
  24. import { themeBorder, themeColor } from '../../helpers/theme';
  25. type AllowedRadioButtonAttributes = Pick<
  26. React.InputHTMLAttributes<HTMLInputElement>,
  27. 'aria-label' | 'autoFocus' | 'id' | 'name' | 'style' | 'title' | 'type'
  28. >;
  29. interface PropsBase extends AllowedRadioButtonAttributes {
  30. checked: boolean;
  31. children?: React.ReactNode;
  32. className?: string;
  33. disabled?: boolean;
  34. }
  35. type Props =
  36. | ({ onCheck: (value: string) => void; value: string } & PropsBase)
  37. | ({ onCheck: () => void; value: never } & PropsBase);
  38. /** @deprecated Use RadioButtonGroup from Echoes instead.
  39. *
  40. * Individual Radio Buttons can't be used anymore.
  41. * Instead, build the list of options and pass it to the RadioButtonGroup component.
  42. */
  43. export function RadioButton({
  44. checked,
  45. children,
  46. className,
  47. disabled,
  48. onCheck,
  49. value,
  50. ...htmlProps
  51. }: Props) {
  52. const handleChange = () => {
  53. if (!disabled) {
  54. onCheck(value);
  55. }
  56. };
  57. return (
  58. <LabelStyled
  59. className={classNames(
  60. {
  61. disabled,
  62. },
  63. className,
  64. )}
  65. >
  66. <RadioButtonStyled
  67. aria-checked={checked}
  68. aria-disabled={disabled}
  69. checked={checked}
  70. disabled={disabled}
  71. onChange={handleChange}
  72. type="radio"
  73. value={value}
  74. {...htmlProps}
  75. />
  76. {children}
  77. </LabelStyled>
  78. );
  79. }
  80. const LabelStyled = styled.label<{ disabled?: boolean }>`
  81. ${tw`sw-flex sw-items-center`}
  82. ${tw`sw-cursor-pointer`}
  83. &.disabled {
  84. color: ${themeColor('radioDisabledLabel')};
  85. ${tw`sw-cursor-not-allowed`}
  86. }
  87. `;
  88. /** @deprecated Use RadioButtonGroup from Echoes instead.
  89. *
  90. * Individual Radio Buttons can't be used anymore.
  91. * Instead, build the list of options and pass it to the RadioButtonGroup component.
  92. */
  93. export const RadioButtonStyled = styled.input`
  94. appearance: none; //disables native style
  95. border: ${themeBorder('default', 'radioBorder')};
  96. ${tw`sw-cursor-pointer`}
  97. ${tw`sw-w-4 sw-min-w-4 sw-h-4 sw-min-h-4`}
  98. ${tw`sw-p-1 sw-mr-2`}
  99. ${tw`sw-inline-block`}
  100. ${tw`sw-box-border`}
  101. ${tw`sw-rounded-pill`}
  102. &:hover {
  103. background: ${themeColor('radioHover')};
  104. }
  105. &:focus,
  106. &:focus-visible {
  107. background: ${themeColor('radioHover')};
  108. border: ${themeBorder('default', 'radioFocusBorder')};
  109. outline: ${themeBorder('focus', 'radioFocusOutline')};
  110. }
  111. &.is-checked,
  112. &:focus:checked,
  113. &:focus-visible:checked,
  114. &:hover:checked,
  115. &:checked {
  116. // Color cannot be used with multiple backgrounds, only image is allowed
  117. background-image: linear-gradient(to right, ${themeColor('radio')}, ${themeColor('radio')}),
  118. linear-gradient(to right, ${themeColor('radioChecked')}, ${themeColor('radioChecked')});
  119. background-clip: content-box, padding-box;
  120. border: ${themeBorder('default', 'radioBorder')};
  121. }
  122. &.is-disabled,
  123. &:disabled {
  124. background: ${themeColor('radioDisabledBackground')};
  125. border: ${themeBorder('default', 'radioDisabledBorder')};
  126. background-clip: unset;
  127. &.is-checked,
  128. &:checked {
  129. background-image: linear-gradient(
  130. to right,
  131. ${themeColor('radioDisabled')},
  132. ${themeColor('radioDisabled')}
  133. ),
  134. linear-gradient(
  135. to right,
  136. ${themeColor('radioDisabledBackground')},
  137. ${themeColor('radioDisabledBackground')}
  138. );
  139. background-clip: content-box, padding-box;
  140. border: ${themeBorder('default', 'radioDisabledBorder')};
  141. }
  142. }
  143. `;