Common front-end components for Web using the Tesseract design system, written for React. https://make.modal.sh/tesseract/web/react/common
 
 
 
 

152 lines
2.9 KiB

  1. import * as React from 'react'
  2. import * as PropTypes from 'prop-types'
  3. import styled from 'styled-components'
  4. import { Icon } from '../../../../react-common/src'
  5. import MenuGraphics, { propTypes as menuGraphicsPropTypes } from '../MenuGraphics/MenuGraphics'
  6. const Link = styled('a')({
  7. display: 'block',
  8. height: 'var(--size-link, 4rem)',
  9. textDecoration: 'none',
  10. position: 'relative',
  11. '::before': {
  12. display: 'block',
  13. content: "''",
  14. position: 'absolute',
  15. top: 0,
  16. left: 0,
  17. width: '100%',
  18. height: '100%',
  19. backgroundColor: 'currentColor',
  20. opacity: 0,
  21. zIndex: -1,
  22. },
  23. '::after': {
  24. display: 'block',
  25. content: "''",
  26. position: 'absolute',
  27. top: 0,
  28. left: 0,
  29. width: '0.25rem',
  30. height: '100%',
  31. backgroundColor: 'currentColor',
  32. opacity: 0,
  33. zIndex: -1,
  34. },
  35. ':hover::before': {
  36. opacity: 0.25,
  37. },
  38. ':hover::after': {
  39. opacity: 0.5,
  40. },
  41. })
  42. const LinkText = styled('span')({
  43. alignSelf: 'center',
  44. display: 'block',
  45. lineHeight: 1.25,
  46. gridColumnStart: 2,
  47. ':first-child': {
  48. gridColumnStart: 1,
  49. },
  50. ':last-child': {
  51. gridColumnEnd: 4,
  52. },
  53. })
  54. const LinkTitle = styled('strong')({
  55. display: 'block',
  56. })
  57. const LinkSubtitle = styled('span')({
  58. display: 'block',
  59. })
  60. const IndicatorContainer = styled('span')({
  61. alignSelf: 'center',
  62. lineHeight: 0,
  63. })
  64. const MenuGraphicsContainer = styled('span')({
  65. display: 'block',
  66. padding: '0.5rem',
  67. lineHeight: 0,
  68. })
  69. export const basePropTypes = {
  70. as: PropTypes.string,
  71. href: PropTypes.string.isRequired,
  72. title: PropTypes.string.isRequired,
  73. subtitle: PropTypes.string,
  74. graphics: PropTypes.shape(menuGraphicsPropTypes),
  75. indicator: PropTypes.string,
  76. onClick: PropTypes.func,
  77. }
  78. const propTypes = {
  79. ...basePropTypes,
  80. enclosingComponent: PropTypes.elementType,
  81. as: PropTypes.elementType,
  82. }
  83. type Props = PropTypes.InferProps<typeof propTypes>
  84. const NavLink = React.forwardRef<HTMLAnchorElement, Props>((
  85. {
  86. enclosingComponent: EnclosingComponent = React.Fragment,
  87. href,
  88. title,
  89. graphics,
  90. subtitle,
  91. indicator,
  92. onClick,
  93. },
  94. ref
  95. ) => (
  96. <Link
  97. ref={ref}
  98. href={href}
  99. onClick={onClick}
  100. >
  101. <EnclosingComponent>
  102. {
  103. graphics as object
  104. && (
  105. <MenuGraphicsContainer>
  106. <MenuGraphics
  107. {...graphics}
  108. />
  109. </MenuGraphicsContainer>
  110. )
  111. }
  112. <LinkText>
  113. <LinkTitle>
  114. {title}
  115. </LinkTitle>
  116. {
  117. subtitle as string
  118. && (
  119. <LinkSubtitle>
  120. {subtitle}
  121. </LinkSubtitle>
  122. )
  123. }
  124. </LinkText>
  125. {
  126. indicator as string
  127. && (
  128. <IndicatorContainer>
  129. <Icon
  130. name={indicator!}
  131. />
  132. </IndicatorContainer>
  133. )
  134. }
  135. </EnclosingComponent>
  136. </Link>
  137. ))
  138. NavLink.propTypes = propTypes
  139. export default NavLink