Common front-end components for Web using the Tesseract design system, written for React. https://make.modal.sh/tesseract/web/react/common
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.
 
 
 
 

155 lines
3.6 KiB

  1. import * as React from 'react'
  2. import * as PropTypes from 'prop-types'
  3. import styled from 'styled-components'
  4. import stringify from '../../services/stringify'
  5. import Icon from '../Icon/Icon'
  6. const Base = styled('div')({
  7. display: 'block',
  8. })
  9. const CaptureArea = styled('label')({
  10. 'marginTop': '0.25rem',
  11. '::after': {
  12. content: '""',
  13. },
  14. })
  15. CaptureArea.displayName = 'label'
  16. const Input = styled('input')({
  17. position: 'absolute',
  18. left: -999999,
  19. width: 1,
  20. height: 1,
  21. })
  22. Input.displayName = 'input'
  23. const IndicatorWrapper = styled('span')({
  24. borderColor: 'var(--color-accent, blue)',
  25. boxSizing: 'border-box',
  26. backgroundColor: 'transparent',
  27. borderRadius: '0.25rem',
  28. position: 'relative',
  29. width: '1.5rem',
  30. height: '1.5rem',
  31. minWidth: '1.5rem',
  32. maxWidth: '1.5rem',
  33. display: 'inline-flex',
  34. verticalAlign: 'top',
  35. justifyContent: 'center',
  36. alignItems: 'center',
  37. cursor: 'pointer',
  38. transitionProperty: 'border-color',
  39. [`${Input}:focus ~ &`]: {
  40. '--color-accent': 'var(--color-active, Highlight)',
  41. },
  42. [`${Input}:disabled ~ &`]: {
  43. cursor: 'not-allowed',
  44. opacity: 0.5,
  45. },
  46. })
  47. const Border = styled('span')({
  48. 'borderColor': 'var(--color-accent, blue)',
  49. 'boxSizing': 'border-box',
  50. 'display': 'inline-block',
  51. 'borderWidth': '0.125rem',
  52. 'borderStyle': 'solid',
  53. 'position': 'absolute',
  54. 'top': 0,
  55. 'left': 0,
  56. 'width': '100%',
  57. 'height': '100%',
  58. 'borderRadius': 'inherit',
  59. 'transitionProperty': 'border-color',
  60. '::before': {
  61. position: 'absolute',
  62. top: 0,
  63. left: 0,
  64. width: '100%',
  65. height: '100%',
  66. content: "''",
  67. borderRadius: '0.125rem',
  68. opacity: 0.5,
  69. pointerEvents: 'none',
  70. },
  71. [`${Base}:focus-within &::before`]: {
  72. boxShadow: '0 0 0 0.375rem var(--color-accent, blue)',
  73. },
  74. })
  75. const Indicator = styled('span')({
  76. backgroundColor: 'var(--color-accent, blue)',
  77. color: 'var(--color-bg, white)',
  78. width: 0,
  79. height: 0,
  80. opacity: 0,
  81. position: 'relative',
  82. boxSizing: 'border-box',
  83. display: 'inline-grid',
  84. placeContent: 'center',
  85. borderRadius: 'inherit',
  86. lineHeight: 1,
  87. transitionProperty: 'background-color, color, width, height, opacity',
  88. [`${Input}:checked + ${IndicatorWrapper} &`]: {
  89. opacity: 1,
  90. width: '100%',
  91. height: '100%',
  92. },
  93. })
  94. const Label = styled('span')({
  95. display: 'block',
  96. verticalAlign: 'top',
  97. float: 'right',
  98. width: 'calc(100% - 2.5rem)',
  99. fontFamily: 'var(--font-family-base)',
  100. pointerEvents: 'none',
  101. })
  102. const LabelContent = styled('span')({
  103. display: 'inline',
  104. pointerEvents: 'auto',
  105. })
  106. const propTypes = {
  107. /**
  108. * Short textual description indicating the nature of the component's value.
  109. */
  110. label: PropTypes.any,
  111. }
  112. type Props = PropTypes.InferProps<typeof propTypes>
  113. /**
  114. * Component for values that have an on/off state.
  115. * @see {@link Select} for a similar component suitable for selecting more values.
  116. * @see {@link RadioButton} for a similar component on selecting a single value among very few choices.
  117. * @type {React.ComponentType<{readonly label?: string} & React.ClassAttributes<unknown>>}
  118. */
  119. const Checkbox = React.forwardRef<HTMLInputElement, Props>(({ label = '', ...etcProps }, ref) => (
  120. <Base>
  121. <CaptureArea>
  122. <Input {...etcProps} ref={ref} type="checkbox" />
  123. <IndicatorWrapper>
  124. <Border />
  125. <Indicator>
  126. <Icon name="check" label="" />
  127. </Indicator>
  128. </IndicatorWrapper>
  129. {typeof label! !== 'undefined' && label !== null && ' '}
  130. <Label>
  131. <LabelContent>{stringify(label)}</LabelContent>
  132. </Label>
  133. </CaptureArea>
  134. </Base>
  135. ))
  136. Checkbox.propTypes = propTypes
  137. Checkbox.displayName = 'Checkbox'
  138. export default Checkbox