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.
 
 
 

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