Design system.
No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.
 
 
 

319 líneas
5.7 KiB

  1. import { css } from '@tesseract-design/goofy-goober';
  2. export enum ButtonSize {
  3. SMALL = 'small',
  4. MEDIUM = 'medium',
  5. LARGE = 'large',
  6. }
  7. export enum ButtonVariant {
  8. OUTLINE = 'outline',
  9. FILLED = 'filled',
  10. }
  11. export type ButtonBaseArgs = {
  12. /**
  13. * Size of the component.
  14. */
  15. size: ButtonSize,
  16. /**
  17. * Will the component occupy the whole width of its container?
  18. */
  19. block: boolean,
  20. /**
  21. * Stylistic variant of the component.
  22. */
  23. variant: ButtonVariant,
  24. /**
  25. * Will the component display a surrounding border?
  26. */
  27. border: boolean,
  28. /**
  29. * Will the component reject any activation?
  30. */
  31. disabled: boolean,
  32. /**
  33. * Will the component conserve visual space?
  34. */
  35. compact: boolean,
  36. /**
  37. * Is the component an item inside a menu?
  38. */
  39. menuItem: boolean,
  40. }
  41. const MIN_HEIGHTS: Record<ButtonSize, string> = {
  42. [ButtonSize.SMALL]: '2.5rem',
  43. [ButtonSize.MEDIUM]: '3rem',
  44. [ButtonSize.LARGE]: '4rem',
  45. };
  46. export const Button = ({
  47. size,
  48. block,
  49. variant,
  50. disabled,
  51. compact,
  52. }: ButtonBaseArgs): string => css.cx(
  53. css`
  54. box-sizing: border-box;
  55. vertical-align: middle;
  56. appearance: none;
  57. font: inherit;
  58. font-family: var(--font-family-base, sans-serif);
  59. text-transform: uppercase;
  60. font-weight: bolder;
  61. border-radius: 0.25rem;
  62. justify-content: center;
  63. align-items: center;
  64. position: relative;
  65. border: 0;
  66. user-select: none;
  67. text-decoration: none;
  68. white-space: nowrap;
  69. line-height: 1;
  70. & > :first-child::before {
  71. box-shadow: 0 0 0 0 var(--color-accent, blue);
  72. transition-property: box-shadow;
  73. transition-duration: 150ms;
  74. transition-timing-function: linear;
  75. }
  76. &:disabled {
  77. opacity: 0.5;
  78. cursor: not-allowed;
  79. }
  80. &::-moz-focus-inner {
  81. outline: 0;
  82. border: 0;
  83. }
  84. &:focus > :first-child::before {
  85. box-shadow: 0 0 0 0.375rem var(--color-accent, blue);
  86. }
  87. &:disabled > :first-child::before {
  88. box-shadow: 0 0 0 0 var(--color-accent, blue) !important;
  89. }
  90. `,
  91. css.dynamic({
  92. 'min-height': MIN_HEIGHTS[size],
  93. }),
  94. css.if (disabled) (
  95. css`
  96. --color-accent: var(--color-primary, blue);
  97. opacity: 0.5;
  98. cursor: not-allowed;
  99. `
  100. ).else (
  101. css`
  102. cursor: pointer;
  103. &:hover {
  104. --color-accent: var(--color-hover, blue);
  105. outline: 0;
  106. }
  107. &:focus {
  108. --color-accent: var(--color-hover, blue);
  109. outline: 0;
  110. }
  111. &:active {
  112. --color-accent: var(--color-active, red);
  113. outline: 0;
  114. }
  115. &:hover > :first-child::before {
  116. box-shadow: 0 0 0 0.375rem var(--color-accent, blue);
  117. }
  118. `
  119. ),
  120. css.if (block) (
  121. css`
  122. width: 100%;
  123. display: flex;
  124. `
  125. ).else (
  126. css`
  127. display: inline-flex;
  128. `
  129. ),
  130. css.if (compact) (
  131. css`
  132. font-stretch: condensed;
  133. padding: 0 0.5rem;
  134. `
  135. ).else(
  136. css`
  137. padding: 0 1rem;
  138. `
  139. ),
  140. css.if (variant === ButtonVariant.FILLED) (
  141. css`
  142. background-color: var(--color-accent, blue);
  143. color: var(--color-bg, white) !important;
  144. `
  145. ),
  146. css.if (variant === ButtonVariant.OUTLINE) (
  147. css`
  148. background-color: var(--color-bg, white);
  149. color: var(--color-accent, blue);
  150. `
  151. ),
  152. );
  153. export const Border = ({
  154. border
  155. }: ButtonBaseArgs): string => css.cx(
  156. css.if (border) (
  157. css`
  158. border-color: var(--color-accent, blue);
  159. box-sizing: border-box;
  160. display: inline-block;
  161. border-width: 0.125rem;
  162. border-style: solid;
  163. position: absolute;
  164. top: 0;
  165. left: 0;
  166. width: 100%;
  167. height: 100%;
  168. border-radius: inherit;
  169. pointer-events: none;
  170. &::before {
  171. position: absolute;
  172. top: 0;
  173. left: 0;
  174. width: 100%;
  175. height: 100%;
  176. content: '';
  177. border-radius: 0.125rem;
  178. opacity: 0.5;
  179. pointer-events: none;
  180. }
  181. `
  182. ),
  183. );
  184. export const Label = ({
  185. compact,
  186. menuItem,
  187. }: ButtonBaseArgs): string => css.cx(
  188. css`
  189. display: block;
  190. flex-grow: 1;
  191. flex-basis: 0;
  192. min-width: 0;
  193. `,
  194. css.if (compact || menuItem) (
  195. css`
  196. text-align: left;
  197. `
  198. ).else (
  199. css`
  200. text-align: center;
  201. `
  202. ),
  203. css.if (compact) (
  204. css`
  205. & ~ :last-child {
  206. margin-right: -0.5rem;
  207. }
  208. `
  209. ).else (
  210. css`
  211. & ~ :last-child {
  212. margin-right: -1rem;
  213. }
  214. `
  215. ),
  216. );
  217. export const BadgeContainer = ({
  218. size,
  219. }: ButtonBaseArgs): string => css.cx(
  220. css`
  221. width: 2rem;
  222. text-align: center;
  223. flex-shrink: 0;
  224. & + * {
  225. margin-left: -0.5rem;
  226. }
  227. `,
  228. css.nest('&:last-child')(
  229. css.dynamic({
  230. width: MIN_HEIGHTS[size],
  231. })
  232. ),
  233. );
  234. export const OverflowText = (): string => css.cx(
  235. css`
  236. width: 100%;
  237. display: block;
  238. overflow: hidden;
  239. text-overflow: ellipsis;
  240. height: 1.1em;
  241. line-height: 1;
  242. `,
  243. );
  244. export const IndicatorWrapper = ({
  245. size
  246. }: ButtonBaseArgs): string => css.cx(
  247. css`
  248. flex-shrink: 0;
  249. box-sizing: border-box;
  250. display: grid;
  251. place-content: center;
  252. padding: 0 1rem;
  253. z-index: 1;
  254. pointer-events: none;
  255. line-height: 1;
  256. user-select: none;
  257. `,
  258. css.dynamic({
  259. width: `calc(${MIN_HEIGHTS[size]} * 0.75)`,
  260. height: MIN_HEIGHTS[size],
  261. }),
  262. );
  263. export const Indicator = () => css.cx(
  264. css`
  265. width: 1.5em;
  266. height: 1.5em;
  267. fill: none;
  268. stroke: currentColor;
  269. stroke-width: 2;
  270. stroke-linecap: round;
  271. stroke-linejoin: round;
  272. `,
  273. );
  274. export const MainText = () => css.cx(
  275. css`
  276. width: 100%;
  277. `,
  278. );
  279. export const Subtext = () => css.cx(
  280. css`
  281. display: block;
  282. height: 1.1em;
  283. line-height: 1.1;
  284. width: 100%;
  285. font-size: 0.875em;
  286. text-transform: none;
  287. font-weight: var(--font-weight-base, normal);
  288. `,
  289. );