Layout scaffolding for Web apps.
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.
 
 
 

148 lines
2.7 KiB

  1. import * as React from 'react'
  2. import styled from 'styled-components'
  3. import {minWidthFactor} from '../../utilities/mixins';
  4. import {configVar} from '../../utilities/helpers'
  5. const Base = styled('div')({
  6. position: 'fixed',
  7. top: 0,
  8. left: 0,
  9. width: '100%',
  10. height: 'var(--height-topbar, 4rem)',
  11. zIndex: 2,
  12. backgroundColor: 'var(--color-bg, white)',
  13. '~ *': {
  14. paddingTop: 'var(--height-topbar, 4rem)',
  15. },
  16. '~ main ~ *': {
  17. paddingTop: 0,
  18. },
  19. [minWidthFactor(3)]: {
  20. '~ main ~ *': {
  21. paddingTop: 'var(--height-topbar, 4rem)',
  22. },
  23. },
  24. })
  25. const Container = styled('div')({
  26. padding: '0 1rem',
  27. boxSizing: 'border-box',
  28. margin: '0 auto',
  29. maxWidth: `calc(${configVar('base-width')} * 2)`,
  30. width: '100%',
  31. height: '100%',
  32. display: 'flex',
  33. alignItems: 'center',
  34. })
  35. const NarrowContainer = styled(Container)({
  36. maxWidth: configVar('base-width'),
  37. })
  38. const WideContainer = styled(Container)({
  39. [minWidthFactor(3)]: {
  40. maxWidth: `calc(${configVar('base-width')} * 3)`,
  41. },
  42. })
  43. const BrandContainer = styled('div')({
  44. })
  45. const CenterContainer = styled('div')({
  46. flex: 'auto',
  47. padding: '0 1rem',
  48. boxSizing: 'border-box',
  49. ':first-child': {
  50. paddingLeft: 0,
  51. },
  52. })
  53. const ActionContainer = styled('div')({
  54. display: 'flex',
  55. alignItems: 'center',
  56. justifyContent: 'flex-end',
  57. height: '100%',
  58. whiteSpace: 'nowrap',
  59. [minWidthFactor(2)]: {
  60. minWidth: '8rem',
  61. },
  62. })
  63. const LinkContainer = styled('div')({
  64. width: 'var(--height-topbar, 4rem)',
  65. height: '100%',
  66. '> *': {
  67. width: '100%',
  68. height: '100%',
  69. display: 'inline-grid',
  70. placeContent: 'center',
  71. },
  72. })
  73. const MenuLinkContainer = styled(LinkContainer)({
  74. [minWidthFactor(3)]: {
  75. position: 'absolute',
  76. left: -999999,
  77. },
  78. })
  79. const CONTAINER_COMPONENTS = {
  80. narrow: NarrowContainer,
  81. normal: Container,
  82. wide: WideContainer,
  83. }
  84. type Props = {
  85. span?: keyof typeof CONTAINER_COMPONENTS,
  86. brand?: React.ReactNode,
  87. menuLink?: React.ReactNode,
  88. userLink?: React.ReactNode,
  89. }
  90. const TopBar: React.FC<Props> = ({
  91. span = 'normal',
  92. brand,
  93. menuLink,
  94. userLink,
  95. children,
  96. }) => {
  97. const { [span as keyof typeof CONTAINER_COMPONENTS]: ContainerComponent = Container } = CONTAINER_COMPONENTS
  98. return (
  99. <Base>
  100. <ContainerComponent>
  101. {
  102. Boolean(brand as unknown)
  103. && (
  104. <BrandContainer>
  105. {brand}
  106. </BrandContainer>
  107. )
  108. }
  109. <CenterContainer>
  110. {children}
  111. </CenterContainer>
  112. <ActionContainer>
  113. {
  114. Boolean(menuLink as unknown)
  115. && (
  116. <MenuLinkContainer>
  117. {menuLink}
  118. </MenuLinkContainer>
  119. )
  120. }
  121. {
  122. Boolean(userLink as unknown)
  123. && (
  124. <LinkContainer>
  125. {userLink}
  126. </LinkContainer>
  127. )
  128. }
  129. </ActionContainer>
  130. </ContainerComponent>
  131. </Base>
  132. )
  133. }
  134. export default TopBar