Template for starting apps, powered by Next.js
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.
 
 

184 lines
3.5 KiB

  1. import * as React from 'react'
  2. import styled from 'styled-components'
  3. import * as T from '@tesseract-design/react-common'
  4. import {UrlObject} from 'url';
  5. const Base = styled('div')({
  6. position: 'fixed',
  7. top: 0,
  8. left: 0,
  9. width: '100%',
  10. height: 'var(--height-topbar, 4rem)',
  11. backgroundColor: 'var(--color-bg, white)',
  12. zIndex: 2,
  13. '@media (prefers-color-scheme: dark)': {
  14. backgroundColor: 'var(--color-bg, black)',
  15. },
  16. '~ *': {
  17. paddingTop: 'var(--height-topbar, 4rem)',
  18. },
  19. '~ main ~ *': {
  20. paddingTop: 0,
  21. },
  22. '@media (min-width: 1080px)': {
  23. '~ main ~ *': {
  24. paddingTop: 'var(--height-topbar, 4rem)',
  25. },
  26. },
  27. })
  28. const Container = styled('div')({
  29. padding: '0 1rem',
  30. boxSizing: 'border-box',
  31. margin: '0 auto',
  32. maxWidth: 'calc(var(--width-base, 360px) * 2)',
  33. width: '100%',
  34. height: '100%',
  35. display: 'flex',
  36. alignItems: 'center',
  37. })
  38. const WideContainer = styled(Container)({
  39. '@media (min-width: 1080px)': {
  40. maxWidth: 'calc(var(--width-base, 360px) * 3)',
  41. },
  42. })
  43. const BrandContainer = styled('div')({
  44. width: '6rem',
  45. '@media (min-width: 720px)': {
  46. width: '8rem',
  47. },
  48. })
  49. const SearchContainer = styled('form')({
  50. flex: 'auto',
  51. padding: '0 1rem',
  52. boxSizing: 'border-box',
  53. ':first-child': {
  54. paddingLeft: 0,
  55. },
  56. })
  57. const UserContainer = styled('div')({
  58. textAlign: 'right',
  59. height: '100%',
  60. whiteSpace: 'nowrap',
  61. '@media (min-width: 720px)': {
  62. minWidth: '8rem',
  63. },
  64. })
  65. const MenuUserLinkContainer = styled('span')({
  66. '@media (min-width: 1080px)': {
  67. position: 'absolute',
  68. left: -999999,
  69. },
  70. })
  71. type Props = {
  72. query?: string,
  73. onSearch?: React.FormEventHandler,
  74. searchLabel?: string,
  75. searchName?: string,
  76. searchHint?: string,
  77. wide?: boolean,
  78. withMenu?: boolean,
  79. brand?: React.ReactNode,
  80. linkComponent?: React.ElementType,
  81. menuLink: UrlObject,
  82. userLink: UrlObject,
  83. menuLinkLabel?: string,
  84. userLinkLabel?: string,
  85. }
  86. const TopBar: React.FC<Props> = ({
  87. query,
  88. onSearch,
  89. searchLabel = 'Search',
  90. searchName = 'q',
  91. searchHint = 'e.g. keywords, names…',
  92. wide,
  93. withMenu,
  94. brand,
  95. linkComponent: LinkComponent = 'a',
  96. menuLink,
  97. menuLinkLabel = 'Menu',
  98. userLink,
  99. userLinkLabel = 'Guest',
  100. }) => {
  101. const ContainerComponent = wide ? WideContainer : Container
  102. return (
  103. <Base>
  104. <ContainerComponent>
  105. {
  106. Boolean(brand)
  107. && (
  108. <BrandContainer>
  109. {brand}
  110. </BrandContainer>
  111. )
  112. }
  113. <SearchContainer
  114. onSubmit={onSearch}
  115. >
  116. <T.TextInput
  117. name={searchName}
  118. label={searchLabel}
  119. hint={searchHint}
  120. defaultValue={query}
  121. border
  122. alternate
  123. indicator={
  124. <T.Icon
  125. name="search"
  126. label=""
  127. />
  128. }
  129. />
  130. </SearchContainer>
  131. <UserContainer>
  132. {
  133. withMenu
  134. && (
  135. <MenuUserLinkContainer>
  136. <LinkComponent
  137. href={menuLink}
  138. style={{
  139. width: 'var(--height-topbar, 4rem)',
  140. height: '100%',
  141. display: 'inline-grid',
  142. placeContent: 'center',
  143. }}
  144. shallow
  145. >
  146. <T.Icon
  147. name="menu"
  148. label={menuLinkLabel}
  149. />
  150. </LinkComponent>
  151. </MenuUserLinkContainer>
  152. )
  153. }
  154. <LinkComponent
  155. href={userLink}
  156. style={{
  157. width: 'var(--height-topbar, 4rem)',
  158. height: '100%',
  159. display: 'inline-grid',
  160. placeContent: 'center',
  161. }}
  162. >
  163. <T.Icon
  164. name="user"
  165. label={userLinkLabel}
  166. />
  167. </LinkComponent>
  168. </UserContainer>
  169. </ContainerComponent>
  170. </Base>
  171. )
  172. }
  173. export default TopBar