Source

adminjs-design-system/src/atoms/button/button-css.tsx

  1. import {
  2. color as styledColor,
  3. space,
  4. typography,
  5. variant,
  6. } from 'styled-system'
  7. import { css } from 'styled-components'
  8. import { cssClass, focusShadowStyle, themeGet } from '../../utils'
  9. import { ButtonProps } from './button-props'
  10. const variantShared = {
  11. color: 'white',
  12. 'border-color': 'transparent',
  13. [`& .${cssClass('Icon')} svg`]: {
  14. fill: 'white',
  15. },
  16. '&:disabled': {
  17. bg: 'grey40',
  18. },
  19. }
  20. const buttonVariants = variant({
  21. variants: {
  22. primary: {
  23. bg: 'primary100',
  24. '&:hover': {
  25. bg: 'hoverBg',
  26. },
  27. className: cssClass(['Button', 'Button_Primary']),
  28. ...variantShared,
  29. },
  30. danger: {
  31. bg: 'error',
  32. '&:hover': {
  33. bg: 'errorDark',
  34. borderColor: 'transparent',
  35. },
  36. className: cssClass(['Button', 'Button_Danger']),
  37. ...variantShared,
  38. },
  39. success: {
  40. bg: 'success',
  41. '&:hover': {
  42. bg: 'successDark',
  43. borderColor: 'transparent',
  44. },
  45. className: cssClass(['Button', 'Button_Success']),
  46. ...variantShared,
  47. },
  48. info: {
  49. bg: 'info',
  50. '&:hover': {
  51. bg: 'infoDark',
  52. borderColor: 'transparent',
  53. },
  54. className: cssClass(['Button', 'Button_Info']),
  55. ...variantShared,
  56. },
  57. secondary: {
  58. bg: 'accent',
  59. className: cssClass(['Button', 'Button_Secondary']),
  60. ...variantShared,
  61. },
  62. light: {
  63. bg: 'white',
  64. className: cssClass(['Button', 'Button_Grey']),
  65. color: 'grey80',
  66. borderColor: 'grey40',
  67. [`& .${cssClass('Icon')} svg`]: {
  68. fill: 'grey80',
  69. },
  70. '&:hover': {
  71. borderColor: 'grey60',
  72. bg: 'grey60',
  73. },
  74. },
  75. text: {
  76. bg: 'transparent',
  77. borderColor: 'transparent',
  78. '&:disabled': {
  79. 'border-color': 'transparent',
  80. },
  81. '&:hover': {
  82. background: 'transparent',
  83. color: 'hoverBg',
  84. borderColor: 'transparent',
  85. textDecoration: 'underline',
  86. },
  87. '&:focus': {
  88. background: 'transparent',
  89. borderColor: 'transparent',
  90. },
  91. '& svg': {
  92. fill: 'primary100',
  93. },
  94. [`&:hover .${cssClass('Icon')} svg`]: {
  95. fill: 'hoverBg',
  96. },
  97. className: cssClass(['Button', 'Button_Text']),
  98. },
  99. },
  100. })
  101. const sizeVariants = variant({
  102. prop: 'size',
  103. variants: {
  104. sm: {
  105. fontSize: 'sm',
  106. py: 'xs',
  107. lineHeight: 'default',
  108. px: 'lg',
  109. [`& .${cssClass('Icon')}`]: {
  110. marginTop: '-1px',
  111. marginBottom: '-1px',
  112. },
  113. },
  114. // md alias default
  115. md: {},
  116. default: {},
  117. lg: {
  118. py: 'default',
  119. px: 'x3',
  120. lineHeight: 'lg',
  121. },
  122. icon: {
  123. py: 'default',
  124. px: 'default',
  125. lineHeight: 'sm',
  126. minWidth: '34px',
  127. height: '34px',
  128. [`& .${cssClass('Icon')} svg`]: {
  129. padding: 0,
  130. margin: 0,
  131. },
  132. },
  133. },
  134. })
  135. const setPointer = (props: React.HTMLProps<HTMLButtonElement>): string => {
  136. if (props.href || props.onClick) {
  137. return 'cursor: pointer'
  138. }
  139. if (props.as === 'a' && !props.href && !props.onClick) {
  140. return 'cursor: auto'
  141. }
  142. return ''
  143. }
  144. /**
  145. * Button CSS Styles which can be reused in another button-like component with styled-components
  146. *
  147. * Usage:
  148. * ```
  149. * import { ButtonCSS } from '@adminjs/design-system'
  150. * import { Link } from 'react-router-dom'
  151. *
  152. * const MyStyledLink = styled(Link)`
  153. * ${ButtonCSS}
  154. * `
  155. * ```
  156. * @memberof Button
  157. * @alias ButtonCSS
  158. */
  159. export const ButtonCSS = css<ButtonProps>`
  160. -webkit-appearance: none;
  161. -moz-appearance: none;
  162. outline: 0;
  163. display: inline-block;
  164. font-family: ${({ theme }): string => theme.font};
  165. line-height: ${themeGet('lineHeights', 'lg')};
  166. vertical-align: middle;
  167. border: 1px solid ${themeGet('colors', 'primary100')};
  168. color: ${themeGet('colors', 'primary100')};
  169. ${(props) => setPointer(props as React.HTMLProps<HTMLButtonElement>)};
  170. text-decoration: none;
  171. padding: ${themeGet('space', 'sm')} ${themeGet('space', 'xxl')};
  172. box-sizing: border-box;
  173. & > .${cssClass('Icon')} {
  174. vertical-align: middle;
  175. }
  176. & > .${cssClass('Icon')} svg {
  177. margin: 0 ${themeGet('space', 'md')} 0 0;
  178. }
  179. & .${cssClass('Icon')} svg {
  180. fill: ${themeGet('colors', 'primary100')};
  181. }
  182. &:hover {
  183. color: ${themeGet('colors', 'white')};
  184. background: ${themeGet('colors', 'hoverBg')};
  185. border-color: ${themeGet('colors', 'hoverBg')};
  186. & .${cssClass('Icon')} svg {
  187. fill: ${themeGet('colors', 'white')};
  188. }
  189. }
  190. &:focus {
  191. border-color: ${themeGet('colors', 'accent')};
  192. ${({ theme }): string => `box-shadow: ${focusShadowStyle(theme)}`};
  193. }
  194. &:disabled {
  195. color: ${themeGet('colors', 'grey60')};
  196. border-color: ${themeGet('colors', 'grey80')};
  197. background: ${themeGet('colors', 'white')};
  198. cursor: default;
  199. & .${cssClass('Icon')} svg {
  200. fill: ${themeGet('colors', 'grey60')};
  201. }
  202. }
  203. ${({ rounded }): string => (rounded ? 'border-radius: 9999px' : '')};
  204. ${styledColor};
  205. ${space};
  206. ${typography};
  207. ${buttonVariants};
  208. ${sizeVariants};
  209. `
  210. export default ButtonCSS