Source

adminjs-design-system/src/atoms/check-box.tsx

  1. import React, { useState, ChangeEvent, useEffect } from 'react'
  2. import styled from 'styled-components'
  3. import { Label } from './label'
  4. import focusShadowStyle from '../utils/focus-shadow.style'
  5. const Icon = styled.svg`
  6. fill: none;
  7. stroke: white;
  8. stroke-width: 2px;
  9. `
  10. export const CheckboxRadioContainer = styled.span`
  11. position: relative;
  12. display: inline-block;
  13. vertical-align: middle;
  14. & + ${Label} {
  15. margin-left: ${({ theme }): string => theme.space.default};
  16. vertical-align: middle;
  17. margin-bottom: ${({ theme }): string => theme.space.sm};
  18. }
  19. `
  20. // Hide checkbox visually but remain accessible to screen readers.
  21. // Source: https://polished.js.org/docs/#hidevisually
  22. const HiddenCheckbox = styled.input.attrs({ type: 'checkbox' })`
  23. border: 0;
  24. clip: rect(0 0 0 0);
  25. height: 1px;
  26. margin: -1px;
  27. overflow: hidden;
  28. padding: 0;
  29. position: absolute;
  30. white-space: nowrap;
  31. width: 1px;
  32. `
  33. type StyledProps = {
  34. checked?: boolean;
  35. disabled?: boolean;
  36. }
  37. const checkboxBackground = (theme, checked, disabled): string => {
  38. if (checked) {
  39. return disabled ? theme.colors.grey40 : theme.colors.primary100
  40. }
  41. return theme.colors.white
  42. }
  43. const StyledCheckbox = styled.a<StyledProps>`
  44. display: inline-block;
  45. width: 16px;
  46. /* when it is placed within a container setting different font size */
  47. font-size: 12px;
  48. cursor: pointer;
  49. border: 1px solid ${({ theme }): string => theme.colors.grey40};
  50. height: 16px;
  51. background: ${({ checked, theme, disabled }): string => checkboxBackground(theme, checked, disabled)};
  52. transition: all 150ms;
  53. position: relative;
  54. ${HiddenCheckbox}:focus + & {
  55. ${({ theme }): string => `box-shadow: ${focusShadowStyle(theme)};`};
  56. }
  57. ${HiddenCheckbox}:hover + & {
  58. border-color: ${({ theme }): string => theme.colors.grey60};
  59. }
  60. ${Icon} {
  61. visibility: ${(props): string => (props.checked ? 'visible' : 'hidden')};
  62. }
  63. &:after {
  64. content: '';
  65. position: absolute;
  66. left: -5px;
  67. top: -5px;
  68. width: 24px;
  69. height: 24px;
  70. opacity: 0;
  71. background: ${({ theme }): string => theme.colors.primary100};
  72. }
  73. &:after:before {
  74. opacity: 0.1;
  75. }
  76. `
  77. export type CheckBoxProps = React.HTMLProps<HTMLInputElement>
  78. /**
  79. * @typedef {object} CheckBoxProps
  80. * @alias CheckBoxProps
  81. * @memberof module:@adminjs/design-system.CheckBox
  82. * @property {string} [...] All props default to _checkbox_ html input like `onChange`,
  83. * `checked` etc.
  84. */
  85. /**
  86. * @classdesc
  87. *
  88. * <img src="components/checkbox.png" />
  89. *
  90. * HTML CheckBox
  91. *
  92. * ### Usage
  93. *
  94. * ```javascript
  95. * import { CheckBox, CheckBoxProps } from '@adminjs/design-system'
  96. * ```
  97. *
  98. * @component
  99. * @see {@link https://storybook.adminjs.co/?path=/story/designsystem-atoms-checkbox--default StoryBook}
  100. * @hideconstructor
  101. * @subcategory Atoms
  102. * @example
  103. * return (
  104. * <Box p="xl">
  105. * <CheckBox id="checkbox1"/>
  106. * <Label inline htmlFor="checkbox1" ml="default">Some example label</Label>
  107. * </Box>
  108. * )
  109. * @section design-system
  110. */
  111. const CheckBox: React.FC<CheckBoxProps> = (props) => {
  112. const { className, checked, onChange, disabled, ...restProps } = props
  113. const [isChecked, setChecked] = useState(checked ?? false)
  114. const handleChange = (event: ChangeEvent<HTMLInputElement>): void => {
  115. if (onChange) {
  116. onChange(event)
  117. } else {
  118. setChecked(!event.target.checked)
  119. }
  120. }
  121. useEffect(() => {
  122. setChecked(checked ?? false)
  123. }, [checked])
  124. return (
  125. <CheckboxRadioContainer className={[className ?? '', 'adminjs_Checkbox'].join(' ')}>
  126. <HiddenCheckbox
  127. checked={isChecked}
  128. onChange={handleChange}
  129. // eslint-disable-next-line @typescript-eslint/ban-types
  130. {...restProps as {}}
  131. disabled={disabled}
  132. />
  133. <StyledCheckbox
  134. checked={isChecked}
  135. disabled={disabled}
  136. onClick={(event): void => handleChange && handleChange(event as any)}
  137. >
  138. <Icon viewBox="0 0 24 24">
  139. <polyline points="20 6 9 17 4 12" />
  140. </Icon>
  141. </StyledCheckbox>
  142. </CheckboxRadioContainer>
  143. )
  144. }
  145. export { CheckBox }
  146. export default CheckBox