Design system.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 

171 lignes
4.8 KiB

  1. import * as React from 'react';
  2. import { tailwind } from '@tesseract-design/web-base';
  3. import { useFallbackId } from '@modal-sh/react-utils';
  4. const { tw } = tailwind;
  5. const ToggleTickBoxDerivedElementComponent = 'input' as const;
  6. /**
  7. * Derived HTML element of the {@link ToggleTickBox} component.
  8. */
  9. export type ToggleTickBoxDerivedElement = HTMLElementTagNameMap[
  10. typeof ToggleTickBoxDerivedElementComponent
  11. ];
  12. /**
  13. * Props of the {@link ToggleTickBox} component.
  14. */
  15. export interface ToggleTickBoxProps extends Omit<React.InputHTMLAttributes<ToggleTickBoxDerivedElement>, 'type' | 'size'> {
  16. /**
  17. * Should the component occupy the whole width of its parent?
  18. */
  19. block?: boolean;
  20. /**
  21. * Complementary content of the component.
  22. */
  23. subtext?: React.ReactNode;
  24. /**
  25. * Is the component in an indeterminate state?
  26. */
  27. indeterminate?: boolean;
  28. }
  29. export const toggleTickBoxPlugin: tailwind.PluginCreator = ({ addComponents, }) => {
  30. addComponents({
  31. '.toggle-tick-box': {
  32. '& + label + label > :first-child > :first-child': {
  33. 'display': 'none',
  34. },
  35. '&:checked + label + label > :first-child > :first-child': {
  36. 'display': 'block',
  37. },
  38. '& + label + label > :first-child > :first-child + *': {
  39. 'display': 'none',
  40. },
  41. '&:indeterminate + label + label > :first-child > :first-child + *': {
  42. 'display': 'block',
  43. },
  44. },
  45. });
  46. };
  47. /**
  48. * Component for toggling a Boolean value with the appearance of a tick box.
  49. */
  50. export const ToggleTickBox = React.forwardRef<ToggleTickBoxDerivedElement, ToggleTickBoxProps>((
  51. {
  52. children,
  53. block = false,
  54. id: idProp,
  55. className,
  56. subtext,
  57. style,
  58. indeterminate = false,
  59. ...etcProps
  60. },
  61. forwardedRef,
  62. ) => {
  63. const defaultRef = React.useRef<ToggleTickBoxDerivedElement>(null);
  64. const ref = forwardedRef ?? defaultRef;
  65. const id = useFallbackId(idProp);
  66. React.useEffect(() => {
  67. if (typeof ref === 'function') {
  68. const defaultElement = defaultRef.current as ToggleTickBoxDerivedElement;
  69. defaultElement.indeterminate = indeterminate;
  70. ref(defaultElement);
  71. return;
  72. }
  73. const element = ref.current as ToggleTickBoxDerivedElement;
  74. element.indeterminate = indeterminate;
  75. }, [indeterminate, defaultRef, ref]);
  76. return (
  77. <div
  78. className={tw(
  79. 'gap-x-4 flex-wrap',
  80. block && 'flex',
  81. !block && 'inline-flex align-center',
  82. className,
  83. )}
  84. style={style}
  85. data-testid="base"
  86. >
  87. <ToggleTickBoxDerivedElementComponent
  88. {...etcProps}
  89. ref={typeof ref === 'function' ? defaultRef : ref}
  90. type="checkbox"
  91. id={id}
  92. className="sr-only peer/radio toggle-tick-box"
  93. />
  94. <label
  95. htmlFor={id}
  96. className="peer/children order-2 cursor-pointer peer-disabled/radio:cursor-not-allowed"
  97. >
  98. <span
  99. data-testid="children"
  100. >
  101. {children}
  102. </span>
  103. </label>
  104. <label
  105. htmlFor={id}
  106. className={tw(
  107. 'order-1 block rounded ring-secondary/50 overflow-hidden gap-4 leading-none select-none cursor-pointer',
  108. 'peer-focus/radio:outline-0 peer-focus/radio:ring-4 peer-focus/radio:ring-secondary/50',
  109. 'active:ring-tertiary/50 active:ring-4',
  110. 'peer-active/children:ring-tertiary/50 peer-active/children:ring-4 peer-active/children:text-tertiary',
  111. 'peer-disabled/radio:opacity-50 peer-disabled/radio:cursor-not-allowed peer-disabled/radio:ring-0',
  112. 'text-primary peer-disabled/radio:text-primary peer-focus/radio:text-secondary peer-checked/radio:text-tertiary active:text-tertiary',
  113. )}
  114. >
  115. <span
  116. className={tw(
  117. 'w-6 h-6 block rounded-inherit border-2 p-0.5 box-border border-current',
  118. )}
  119. >
  120. <svg
  121. className={tw(
  122. 'w-full h-full fill-none stroke-3 linejoin-round linecap-round stroke-current',
  123. )}
  124. viewBox="0 0 24 24"
  125. role="presentation"
  126. >
  127. <polyline
  128. points="20 6 9 17 4 12"
  129. />
  130. </svg>
  131. <svg
  132. className={tw(
  133. 'w-full h-full fill-none stroke-3 linejoin-round linecap-round stroke-current',
  134. )}
  135. viewBox="0 0 24 24"
  136. role="presentation"
  137. >
  138. <polyline
  139. points="20 12 4 12"
  140. />
  141. </svg>
  142. </span>
  143. </label>
  144. {subtext && (
  145. <div
  146. className="block w-full text-xs pl-10 order-3"
  147. data-testid="subtext"
  148. >
  149. {subtext}
  150. </div>
  151. )}
  152. </div>
  153. );
  154. });
  155. ToggleTickBox.displayName = 'ToggleTickBox';
  156. ToggleTickBox.defaultProps = {
  157. block: false,
  158. indeterminate: false,
  159. subtext: undefined,
  160. };