react-feather#X JavaScript Examples

The following examples show how to use react-feather#X. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: Popup.js    From ecomloop.github.io with MIT License 6 votes vote down vote up
render() {
    const { children } = this.props
    return (
      <Fragment>
        <div className="taCenter">
          <div class="Button" onClick={this.togglePopup.bind(this)}>
            start &#123;next&#125; project
          </div>
        </div>

        {this.state.showPopup ? (
          <div className="Popup-Overlay">
            <div
              className="Popup-Background"
              onClick={this.togglePopup.bind(this)}
            ></div>
            <div className="Popup-Inner">
              <X class="Popup-Close" onClick={this.togglePopup.bind(this)} />
              <div class="klaviyo-form-SPsAZ9">uh oh..too late now! (just kidding...try a refresh)</div>
            </div>
          </div>
        ) : null}
      </Fragment>
    )
  }
Example #2
Source File: index.js    From spooky-info with GNU General Public License v3.0 6 votes vote down vote up
CloseIcon = styled(X)`
  height: 20px;
  width: 20px;
  margin-right: 1rem;
  position: absolute;
  right: 10px;
  color: ${({ theme }) => theme.text3};
  :hover {
    cursor: pointer;
  }
`
Example #3
Source File: CloseButton.js    From hivemind with Apache License 2.0 6 votes vote down vote up
closeButton = ({ popperKey, divKey, poppers }) => (
  <CardLink
    href="#"
    className="btn btn-outline-dark float-right align-bottom ml-1"
    onClick={() => removePopper(popperKey, divKey, poppers)}
  >
    <X />
  </CardLink>
)
Example #4
Source File: AlertMessage.js    From spotify-react with MIT License 6 votes vote down vote up
render() {
    const {message} = this.props;
    return (
      <div className="alert__wrapper">
        <span className="alert__message">{message}</span>
        <span
          className="alert__close"
          onClick={this.remove}
        >
          <X/>
        </span>
      </div>
    );
  }
Example #5
Source File: SearchInput.js    From spotify-react with MIT License 6 votes vote down vote up
render() {
    const {pending, value, updateSearchValue, close} = this.props;
    return (
      <div className="search__input-container flex-center">
        <div className="search__input-wrapper">
          <p>What are you looking for?</p>
          <div className="search__input">
            <input
              onChange={updateSearchValue}
              type='text'
              placeholder='Start Typing...'
              className="search__input-field"
              value={value}
            />
            {pending &&
              <span className="search__loader"><Loader /></span>
            }
            <span
              className="search__close"
              onClick={close}
            >
              <X/>
            </span>
          </div>
        </div>
      </div>
    );
  }
Example #6
Source File: index.js    From pancake-info-v1 with GNU General Public License v3.0 6 votes vote down vote up
CloseIcon = styled(X)`
  height: 20px;
  width: 20px;
  margin-right: 0.5rem;
  position: absolute;
  right: 10px;
  color: ${({ theme }) => theme.text3};
  :hover {
    cursor: pointer;
  }
`
Example #7
Source File: navigation.jsx    From ambition-fund-website with MIT License 5 votes vote down vote up
render() {
    const { mobileMenuOpen } = this.state

    return (
      <Nav {...this.props} scrolled={this.state.hasScrolled}>
        <StyledContainer>
          <Brand>
            <Scrollspy offset={-64} item={["top"]} currentClassName="active">
              <Image />
              {this.getNavLink({
                name: "Ambition Fund",
                pathname: "/",
                hash: "#top",
              })}
            </Scrollspy>
          </Brand>
          <Mobile>
            <button
              onClick={this.toggleMobileMenu}
              style={{ color: "black", background: "none" }}
            >
              {this.state.mobileMenuOpen ? (
                <X size={24} alt="close menu" />
              ) : (
                <Menu size={24} alt="open menu" />
              )}
            </button>
          </Mobile>

          <Mobile hide>{this.getNavList({})}</Mobile>
        </StyledContainer>
        <Mobile>
          {mobileMenuOpen && (
            <MobileMenu>
              <Container>{this.getNavList({ mobile: true })}</Container>
            </MobileMenu>
          )}
        </Mobile>
      </Nav>
    )
  }
Example #8
Source File: Navbar.js    From spotify-react with MIT License 5 votes vote down vote up
render() {
    const navbarItems = [
      { path: RouteConstant.HOME, icon: <Home />, name: "Home" },
      { path: RouteConstant.CHARTS, icon: <Lightning />, name: "Charts" },
      { path: RouteConstant.NEW, icon: <Label />, name: "New" },
      { path: RouteConstant.GENRES, icon: <LightBulb />, name: "Genres" },
      { path: RouteConstant.PLAYLISTS, icon: <Layers />, name: "Playlists" },
      { path: RouteConstant.LIKED, icon: <Heart />, name: "Songs" },
      { path: RouteConstant.ARTISTS, icon: <User />, name: "Artists" },
    ];
    const {isHidden, isMenuOpen} = this.state;
    return (
      <React.Fragment>
        {isHidden &&
          <div
            className="navbar__open-btn flex-center fixed-btn"
            onClick={this.toggleMenu}>
            {isMenuOpen ? <X /> : <Menu/>}
          </div>
        }
        <nav className={`app__navbar ${isHidden
          ? `app__navbar_mobile${isMenuOpen ? "_open" : ""}`
          : ""
        }`}>
          <div className="navbar">
            <div className="navbar__logo">
              <img className="navbar__logo-img" src={logo} alt=""/>
              <SearchBtn className="navbar__search"/>
            </div>
            <ul className="navbar__group">
              {navbarItems.slice(0, 4).map(({path, icon, name}, index) => {
                return (
                  <li
                    key={index}
                    onClick={() => {
                     isMenuOpen && this.toggleMenu();
                    }}
                  >
                    <NavLink
                      exact
                      to={path}
                      className="navbar__item"
                      activeClassName="active"
                    >
                      <span className="navbar__icon">{icon}</span>
                      {name}
                    </NavLink>
                  </li>
                );
              })}
            </ul>
            <ul className="navbar__group">
              <p className="navbar__group-header">YOUR LIBRARY</p>
              {navbarItems.slice(4, 7).map(({path, icon, name}, index) => {
                return (
                  <li
                    key={index + 4}
                    onClick={() => {
                     isMenuOpen && this.toggleMenu();
                    }}
                  >
                    <NavLink
                      exact
                      to={path}
                      className="navbar__item"
                      activeClassName="active"
                    >
                      <span className="navbar__icon">{icon}</span>
                      {name}
                    </NavLink>
                  </li>
                );
              })}
            </ul>
          </div>
        </nav>
      </React.Fragment>
    );
  }
Example #9
Source File: index.js    From docz-theme-extended with MIT License 5 votes vote down vote up
Search = ({open, toggleOpen, className, ...rest}) => {
  const classes = `${open ? 'open' : ''} ${className}`;
  const [results, setResults] = useState(null);
  const docs = useDocs();

  const search = (ev) => {
    ev.preventDefault();
    const form = new FormData(ev.target);
    const q = form.get('query');
    if (!q || !q.length) {
      setResults(null);
      return;
    }
    const regex = new RegExp(q, 'i');
    const filtered = docs.filter(filter.bind(null, regex));
    setResults(filtered);
  };

  return (
    <DrawerContainer className={classes} {...rest}>
      <div>
        <Close onClick={() => toggleOpen(false)}>
          <X size={22} />
        </Close>
        <SearchContainer>
          <form action="" onSubmit={search}>
            <label
              htmlFor="searchQuery"
              aria-hidden="true"
              style={{display: 'none'}}
            >
              Search
            </label>
            <input
              id="searchQuery"
              name="query"
              type="text"
              placeholder="Type to search..."
              aria-label="Query"
            />
            <Submit type="submit" aria-label="Submit">
              <ArrowRight size={22} />
            </Submit>
          </form>
        </SearchContainer>
        {results && results.length ? (
          <Results>
            {results.map((doc, i) => (
              <Link key={i} to={doc.route}>
                <h4>{doc.name}</h4>
                <span>{doc.route}</span>
              </Link>
            ))}
          </Results>
        ) : null}
      </div>
    </DrawerContainer>
  );
}
Example #10
Source File: CustomTable.jsx    From CRM with Apache License 2.0 5 votes vote down vote up
CustomTable = ({ columns, data, title }) => {
  const tableIcons = {
    Add: forwardRef((props, ref) => <Plus {...props} ref={ref} />),
    Check: forwardRef((props, ref) => <Check {...props} ref={ref} />),
    Clear: forwardRef((props, ref) => <X {...props} ref={ref} />),
    Delete: forwardRef((props, ref) => <Trash {...props} ref={ref} />),
    DetailPanel: forwardRef((props, ref) => (
      <ChevronRight {...props} ref={ref} />
    )),
    Edit: forwardRef((props, ref) => <Edit2 {...props} ref={ref} />),
    Export: forwardRef((props, ref) => <Save {...props} ref={ref} />),
    Filter: forwardRef((props, ref) => (
      <Filter {...props} ref={ref} strokeWidth={1.5} width={15} />
    )),
    FirstPage: forwardRef((props, ref) => (
      <ChevronsLeft {...props} ref={ref} />
    )),
    LastPage: forwardRef((props, ref) => (
      <ChevronsRight {...props} ref={ref} />
    )),
    NextPage: forwardRef((props, ref) => <ChevronRight {...props} ref={ref} />),
    PreviousPage: forwardRef((props, ref) => (
      <ChevronLeft {...props} ref={ref} />
    )),
    ResetSearch: forwardRef((props, ref) => <X {...props} ref={ref} />),
    Search: forwardRef((props, ref) => (
      <Search {...props} ref={ref} strokeWidth={1.5} width={18} />
    )),
    SortArrow: forwardRef((props, ref) => (
      <ChevronsDown {...props} ref={ref} />
    )),
    ThirdStateCheck: forwardRef((props, ref) => (
      <XCircle {...props} ref={ref} />
    )),
    ViewColumn: forwardRef((props, ref) => <Eye {...props} ref={ref} />),
  };

  return (
    <React.Fragment>
      <MaterialTable
        icons={tableIcons}
        columns={columns}
        data={data}
        title={title}
        options={{
          filtering: true,
          sorting: true,
          grouping: true,
          exportButton: true,
          headerStyle: {
            backgroundColor: "#3358f4",
            background: "linear-gradient(90deg, #3358f4, #1d8cf8)",
            color: "#FFF",
            backgroundRepeat: "no-repeat",
            textTransform: "uppercase",
          },
          rowStyle: (rowData) => ({
            backgroundColor: "rgb(0,0,0,0)",
          }),
        }}
      />
    </React.Fragment>
  );
}
Example #11
Source File: Nav.js    From ecomloop.github.io with MIT License 4 votes vote down vote up
render() {
        const { active } = this.state,
            { subNav, data } = this.props,
            NavLink = ({ to, className, children, ...props }) => (
                <Link
                    to={to}
                    className={`NavLink ${
                        to === this.state.currentPath ? 'active' : ''
                        } ${className}`}
                    onClick={this.handleLinkClick}
                    {...props}
                >
                    {children}
                </Link>
            )

        return (
            <nav className={`Nav ${active ? 'Nav-active' : ''}`}>
                <div className="Nav--Container container">
                    <Link to="/" onClick={this.handleLinkClick}>
                        <Logo />
                    </Link>
                    <div className="Nav--Links">

                        {
                            <div className={`Nav--Group ${this.state.activeSubNav === 'posts' ? 'active' : ''}`}>
                                <span
                                    className={`Nav--GroupParent ${this.props.location.pathname.includes('solution')? 'active': ''}`}
                                    onClick={() => this.toggleSubNav('posts')}
                                >
                                    what we do
                                    <div className="Nav--GroupLinks">



                                        <NavLink to="/ai-integration/" className="Nav--GroupLink">
                                            AI integration
                                        </NavLink>
                                        <NavLink to="/api-integration-services/" className="Nav--GroupLink">
                                            API integration services
                                        </NavLink>
                                        <NavLink to="/nft-consulting/" className="Nav--GroupLink">
                                          Conversion Rate Optimization
                                        </NavLink>

                                        <NavLink to="/crypto-consulting/" className="Nav--GroupLink">
                                            Crypto Consulting
                                        </NavLink>
                                        <NavLink to="/digital-strategy-consulting/" className="Nav--GroupLink">
                                            Digital strategy consulting
                                        </NavLink>

                                        <NavLink to="/metaverse-vr-consulting/" className="Nav--GroupLink">
                                            Metaverse & VR consulting
                                        </NavLink>
                                        <NavLink to="/web-3-development/" className="Nav--GroupLink">
                                            Mobile & web 3 development
                                        </NavLink>

                                        <NavLink to="/nft-consulting/" className="Nav--GroupLink">
                                            NFT consulting
                                        </NavLink>




                                        {/*
                                        <NavLink to="/ecommerce-services/" className="Nav--GroupLink">
                                            Ecommerce agency services
                                        </NavLink>
                                        <NavLink to="/digital-platform-development/" className="Nav--GroupLink">
                                            Digital platform development
                                        </NavLink>
                                        */}
                                    </div>

                                    {/*<div className="Nav--GroupLinks">
                                        <NavLink to="/solutions/" className="Nav--GroupLink">
                                            All Solutions
                                        </NavLink>
                                        {subNav.products.map((link, index) => (
                                            <NavLink
                                            to={`/solution/${link.handle}/`}
                                            key={'posts-subnav-link-' + index}
                                            className="Nav--GroupLink"
                                            >
                                            {link.title}
                                            </NavLink>
                                        ))}
                                    </div>
                                    */}
                                </span>
                            </div>
                        }

                        {
                            <div className={`Nav--Group ${this.state.activeSubNav === 'tools' ? 'active' : ''}`}>
                                <span
                                    className={`Nav--GroupParent ${this.props.location.pathname.includes('tools')? 'active': ''}`}
                                    onClick={() => this.toggleSubNav('tools')}
                                >
                                    tools
                                    <div className="Nav--GroupLinks">


                                            <NavLink
                                            to={`https://headless.ecomloop.com/`}
                                            key={'headless'}
                                            className="Nav--GroupLink"
                                            target="_blank"
                                            >
                                            Headless // gatsby + shopify theme
                                            </NavLink>
                                            <NavLink
                                            to={`https://www.gatsbyjs.com/plugins/gatsby-plugin-klaviyo/`}
                                            key={'klatsby'}
                                            className="Nav--GroupLink"
                                            target="_blank"
                                            >
                                            Klatsby // klayvio plugin for gatsby
                                            </NavLink>
                                            <NavLink
                                            to={`#`}
                                            key={'#'}
                                            className="Nav--GroupLink"
                                            target="_blank"
                                            >
                                            Shoptomo // shopify analtyics app
                                            </NavLink>

                                    </div>
                                </span>
                            </div>
                        }


                        <NavLink to="/blog/" title="ecomloop blog">blog</NavLink>

                        <NavLink to="/contact/" title="digital commerce solutions" className="Nav--CTA animated rubberBand slow delay-5s">  start &#123;next&#125; project</NavLink>
                    </div>
                    <button
                        className="Nav--MenuButton"
                        onClick={this.handleMenuToggle}
                        to='/home'
                    >
                        {active ? <X /> : <Menu />}
                    </button>
                </div>
            </nav>
        )
    }
Example #12
Source File: index.js    From spooky-info with GNU General Public License v3.0 4 votes vote down vote up
function AccountSearch({ history, small }) {
  const [accountValue, setAccountValue] = useState()
  const [savedAccounts, addAccount, removeAccount] = useSavedAccounts()

  function handleAccountSearch() {
    if (isAddress(accountValue)) {
      history.push('/account/' + accountValue)
      if (!savedAccounts.includes(accountValue)) {
        addAccount(accountValue)
      }
    }
  }

  return (
    <AutoColumn gap={'1rem'}>
      {!small && (
        <>
          <AutoRow>
            <Wrapper>
              <Input
                placeholder="0x..."
                onChange={(e) => {
                  setAccountValue(e.target.value)
                }}
              />
            </Wrapper>
            <ButtonLight onClick={handleAccountSearch}>Load Account Details</ButtonLight>
          </AutoRow>
        </>
      )}

      <AutoColumn gap={'12px'}>
        {!small && (
          <Panel>
            <DashGrid center={true} style={{ height: 'fit-content', padding: '0 0 1rem 0' }}>
              <TYPE.main area="account">Saved Accounts</TYPE.main>
            </DashGrid>
            <Divider />
            {savedAccounts?.length > 0 ? (
              savedAccounts.map((account) => {
                return (
                  <DashGrid key={account} center={true} style={{ height: 'fit-content', padding: '1rem 0 0 0' }}>
                    <Flex
                      area="account"
                      justifyContent="space-between"
                      onClick={() => history.push('/account/' + account)}
                    >
                      <AccountLink>{account?.slice(0, 42)}</AccountLink>
                      <Hover
                        onClick={(e) => {
                          e.stopPropagation()
                          removeAccount(account)
                        }}
                      >
                        <StyledIcon>
                          <X size={16} />
                        </StyledIcon>
                      </Hover>
                    </Flex>
                  </DashGrid>
                )
              })
            ) : (
              <TYPE.light style={{ marginTop: '1rem' }}>No saved accounts</TYPE.light>
            )}
          </Panel>
        )}

        {small && (
          <>
            <TYPE.main>{'Accounts'}</TYPE.main>
            {savedAccounts?.length > 0 ? (
              savedAccounts.map((account) => {
                return (
                  <RowBetween key={account}>
                    <ButtonFaded onClick={() => history.push('/account/' + account)}>
                      {small ? (
                        <TYPE.header>{account?.slice(0, 6) + '...' + account?.slice(38, 42)}</TYPE.header>
                      ) : (
                        <AccountLink>{account?.slice(0, 42)}</AccountLink>
                      )}
                    </ButtonFaded>
                    <Hover onClick={() => removeAccount(account)}>
                      <StyledIcon>
                        <X size={16} />
                      </StyledIcon>
                    </Hover>
                  </RowBetween>
                )
              })
            ) : (
              <TYPE.light>No pinned wallets</TYPE.light>
            )}
          </>
        )}
      </AutoColumn>
    </AutoColumn>
  )
}
Example #13
Source File: index.js    From spooky-info with GNU General Public License v3.0 4 votes vote down vote up
function PinnedData({ history, open, setSavedOpen }) {
  const [savedPairs, , removePair] = useSavedPairs()
  const [savedTokens, , removeToken] = useSavedTokens()

  return !open ? (
    <RightColumn open={open} onClick={() => setSavedOpen(true)}>
      <SavedButton open={open}>
        <StyledIcon>
          <Bookmark size={20} />
        </StyledIcon>
      </SavedButton>
    </RightColumn>
  ) : (
    <RightColumn gap="1rem" open={open}>
      <SavedButton onClick={() => setSavedOpen(false)} open={open}>
        <RowFixed>
          <StyledIcon>
            <Bookmark size={16} />
          </StyledIcon>
          <TYPE.main ml={'4px'}>Saved</TYPE.main>
        </RowFixed>
        <StyledIcon>
          <ChevronRight />
        </StyledIcon>
      </SavedButton>
      <AccountSearch small={true} />
      <AutoColumn gap="40px" style={{ marginTop: '2rem' }}>
        <AutoColumn gap={'12px'}>
          <TYPE.main>Pinned Pairs</TYPE.main>
          {Object.keys(savedPairs).filter((key) => {
            return !!savedPairs[key]
          }).length > 0 ? (
            Object.keys(savedPairs)
              .filter((address) => {
                return !!savedPairs[address]
              })
              .map((address) => {
                const pair = savedPairs[address]
                return (
                  <RowBetween key={pair.address}>
                    <ButtonFaded onClick={() => history.push('/pair/' + address)}>
                      <RowFixed>
                        <TYPE.header>
                          <FormattedName
                            text={pair.token0Symbol + '/' + pair.token1Symbol}
                            maxCharacters={12}
                            fontSize={'12px'}
                          />
                        </TYPE.header>
                      </RowFixed>
                    </ButtonFaded>
                    <Hover onClick={() => removePair(pair.address)}>
                      <StyledIcon>
                        <X size={16} />
                      </StyledIcon>
                    </Hover>
                  </RowBetween>
                )
              })
          ) : (
            <TYPE.light>Pinned pairs will appear here.</TYPE.light>
          )}
        </AutoColumn>
        <ScrollableDiv gap={'12px'}>
          <TYPE.main>Pinned Tokens</TYPE.main>
          {Object.keys(savedTokens).filter((key) => {
            return !!savedTokens[key]
          }).length > 0 ? (
            Object.keys(savedTokens)
              .filter((address) => {
                return !!savedTokens[address]
              })
              .map((address) => {
                const token = savedTokens[address]
                return (
                  <RowBetween key={address}>
                    <ButtonFaded onClick={() => history.push('/token/' + address)}>
                      <RowFixed>
                        <TokenLogo address={address} size={'14px'} />
                        <TYPE.header ml={'6px'}>
                          <FormattedName text={token.symbol} maxCharacters={12} fontSize={'12px'} />
                        </TYPE.header>
                      </RowFixed>
                    </ButtonFaded>
                    <Hover onClick={() => removeToken(address)}>
                      <StyledIcon>
                        <X size={16} />
                      </StyledIcon>
                    </Hover>
                  </RowBetween>
                )
              })
          ) : (
            <TYPE.light>Pinned tokens will appear here.</TYPE.light>
          )}
        </ScrollableDiv>
      </AutoColumn>
    </RightColumn>
  )
}
Example #14
Source File: index.js    From pancake-info-v1 with GNU General Public License v3.0 4 votes vote down vote up
function AccountSearch({ history, small }) {
  const [accountValue, setAccountValue] = useState()
  const [savedAccounts, addAccount, removeAccount] = useSavedAccounts()

  function handleAccountSearch() {
    if (isAddress(accountValue)) {
      history.push('/account/' + accountValue)
      if (!savedAccounts.includes(accountValue)) {
        addAccount(accountValue)
      }
    }
  }

  return (
    <AutoColumn gap={'1rem'}>
      {!small && (
        <>
          <AutoRow>
            <Wrapper>
              <Input
                placeholder="0x..."
                onChange={(e) => {
                  setAccountValue(e.target.value)
                }}
              />
            </Wrapper>
            <ButtonLight onClick={handleAccountSearch}>Load Account Details</ButtonLight>
          </AutoRow>
        </>
      )}

      <AutoColumn gap={'12px'}>
        {!small && (
          <Panel>
            <DashGrid center={true} style={{ height: 'fit-content', padding: '0 0 1rem 0' }}>
              <TYPE.main area="account">Saved Accounts</TYPE.main>
            </DashGrid>
            <Divider />
            {savedAccounts?.length > 0 ? (
              savedAccounts.map((account) => {
                return (
                  <DashGrid key={account} center={true} style={{ height: 'fit-content', padding: '1rem 0 0 0' }}>
                    <Flex
                      area="account"
                      justifyContent="space-between"
                      onClick={() => history.push('/account/' + account)}
                    >
                      <AccountLink>{account?.slice(0, 42)}</AccountLink>
                      <Hover
                        onClick={(e) => {
                          e.stopPropagation()
                          removeAccount(account)
                        }}
                      >
                        <StyledIcon>
                          <X size={16} />
                        </StyledIcon>
                      </Hover>
                    </Flex>
                  </DashGrid>
                )
              })
            ) : (
              <TYPE.light style={{ marginTop: '1rem' }}>No saved accounts</TYPE.light>
            )}
          </Panel>
        )}

        {small && (
          <>
            <TYPE.main>{'Accounts'}</TYPE.main>
            {savedAccounts?.length > 0 ? (
              savedAccounts.map((account) => {
                return (
                  <RowBetween key={account}>
                    <ButtonFaded onClick={() => history.push('/account/' + account)}>
                      {small ? (
                        <TYPE.header>{account?.slice(0, 6) + '...' + account?.slice(38, 42)}</TYPE.header>
                      ) : (
                        <AccountLink>{account?.slice(0, 42)}</AccountLink>
                      )}
                    </ButtonFaded>
                    <Hover onClick={() => removeAccount(account)}>
                      <StyledIcon>
                        <X size={16} />
                      </StyledIcon>
                    </Hover>
                  </RowBetween>
                )
              })
            ) : (
              <TYPE.light>No pinned wallets</TYPE.light>
            )}
          </>
        )}
      </AutoColumn>
    </AutoColumn>
  )
}
Example #15
Source File: Docs.jsx    From vertx-web-site.github.io with Apache License 2.0 4 votes vote down vote up
Docs = ({ metadata, allVersions, fallbackGitHubStars, toc, contents }) => {
  const tocRef = useRef()
  const searchResultsRef = useRef()
  const contentRef = useRef()
  const sidebarRef = useRef()
  const sidebarAutoHideTimer = useRef(null)
  const [sidebarCollapse, setSidebarCollapse] = useState(false)
  const [hasSearchResults, setHasSearchResults] = useState()
  const currentVersion = useContext(VersionContext.State)
  const sortedAllVersions = allVersions.sort().reverse()
  const activeVersion = currentVersion.version !== undefined ? currentVersion.version : latestRelease.version
  const activeVersionTitle = docsMetadata.find(m => m.version === activeVersion)
      .metadata.title || activeVersion

  const enableBodyScrollInternal = () => {
    enableBodyScroll(tocRef.current)
    enableBodyScroll(searchResultsRef.current)
  }

  const disableBodyScrollInternal = () => {
    // Do not disable body scroll if the window has a scrollbar (typically on
    // Windows). Otherwise, the scrollbar will disappear and the content will
    // jump to the right.
    let hasScrollbar = window.innerWidth > document.documentElement.clientWidth
    if (!hasScrollbar) {
      disableBodyScroll(searchResultsRef.current)
      disableBodyScroll(tocRef.current)
    }
  }

  const cancelSidebarAutoHideTimer = useCallback(() => {
    if (sidebarAutoHideTimer.current) {
      clearTimeout(sidebarAutoHideTimer.current)
      sidebarAutoHideTimer.current = null
    }
  }, [])

  const startSidebarAutoHideTimer = useCallback(() => {
    cancelSidebarAutoHideTimer()
    sidebarAutoHideTimer.current = setTimeout(() => {
      setSidebarCollapse(false)
      sidebarAutoHideTimer.current = null
    }, 500)
  }, [cancelSidebarAutoHideTimer])

  const onHashChangeStart = useCallback((url, initial) => {
    enableBodyScrollInternal()
    cancelSidebarAutoHideTimer()
    setSidebarCollapse(false)

    let hash = url.substring(url.indexOf("#") + 1)
    let target = document.getElementById(hash)
    if (!target) {
      return
    }

    // make it so that the search box and the element are vertically centered
    let computedStyle = window.getComputedStyle(target)
    let paddingTop = parseInt(computedStyle.paddingTop)
    let lineHeight = parseInt(computedStyle.lineHeight)
    let sidebarStyle = window.getComputedStyle(sidebarRef.current)
    let sidebarTop = parseInt(sidebarStyle.top)
    let offset = target.offsetTop - sidebarTop +
        paddingTop + (lineHeight / 2 - 20)

    smoothScrollTo(offset, initial ? 200 : 500)
  }, [cancelSidebarAutoHideTimer])

  const onHashChange = useCallback((e) => {
    onHashChangeStart(e.newURL)
  }, [onHashChangeStart])

  const onSidebarMouseEnter = useCallback(() => {
    cancelSidebarAutoHideTimer()
    disableBodyScrollInternal()
  }, [cancelSidebarAutoHideTimer])

  const onSidebarMouseLeave = useCallback(() => {
    enableBodyScrollInternal()
    startSidebarAutoHideTimer()
  }, [startSidebarAutoHideTimer])

  const onContentMouseDown = () => {
    onSidebarMouseLeave()
  }

  const onContentTouchStart = () => {
    onSidebarMouseLeave()
  }

  const onSidebarToggle = () => {
    if (sidebarCollapse) {
      enableBodyScrollInternal()
    } else {
      disableBodyScrollInternal()
    }
    cancelSidebarAutoHideTimer()
    setSidebarCollapse(!sidebarCollapse)
  }

  // replace internal links' onclick with Router.push() so we can scroll smoothly
  const replaceInternalLinks = useCallback((ref) => {
    let internalLinks = ref.current.querySelectorAll("a[href^='#']")
    for (let il of internalLinks) {
      il.onclick = (e) => {
        e.preventDefault()
        let href = window.location.href
        let hash = href.substring(href.indexOf("#"))
        if (hash !== il.getAttribute("href")) {
          Router.push(window.location.pathname + il.getAttribute("href"))
        } else {
          onHashChangeStart(href)
        }
      }
    }
  }, [onHashChangeStart])

  useEffect(() => {
    Router.events.on("hashChangeStart", onHashChangeStart)
    window.addEventListener("hashchange", onHashChange)

    let sidebar = sidebarRef.current
    sidebar.addEventListener("mouseenter", onSidebarMouseEnter)
    sidebar.addEventListener("mouseleave", onSidebarMouseLeave)

    replaceInternalLinks(tocRef)
    replaceInternalLinks(contentRef)

    // initial scroll
    onHashChangeStart(window.location.href, true)

    return () => {
      sidebar.removeEventListener("mouseenter", onSidebarMouseEnter)
      sidebar.removeEventListener("mouseleave", onSidebarMouseLeave)
      window.removeEventListener("hashchange", onHashChange)
      Router.events.off("hashChangeStart", onHashChangeStart)
      clearAllBodyScrollLocks()
    }
  }, [onHashChange, onHashChangeStart, onSidebarMouseEnter, onSidebarMouseLeave,
      replaceInternalLinks])

  let repository
  if (metadata.repository) {
    let m = metadata.repository.match(/https?:\/\/github\.com\/([^/]+)\/([^/]+)/)
    if (m) {
      let org = m[1]
      let repo = m[2]
      repository = <GitHubStars org={org} repo={repo} fallbackValue={fallbackGitHubStars} />
    } else {
      repository = <a href={metadata.repository}><Code className="feather" /> Source code</a>
    }
  }

  let examples
  if (metadata.examples) {
    examples = <a href={metadata.examples}><Paperclip className="feather" /> Examples</a>
  }
  let edit
  if (metadata.edit) {
    edit = <a href={metadata.edit}><Edit className="feather" /> Edit</a>
  }

  return (
    <main className="page docs">
      <Header title={metadata.name}/>
      <div className="page-content docs-content">
        <div className="container">
          <div className="docs-content-wrapper">
            <aside className={classNames({ "docs-has-search-results": hasSearchResults, "collapse": sidebarCollapse })}>
              <div className="docs-content-wrapper-sidebar" ref={sidebarRef}>
                <SearchPanel contentRef={contentRef} onHasResults={setHasSearchResults} ref={searchResultsRef} />
                <div dangerouslySetInnerHTML={{ __html: toc }} ref={tocRef}
                    className="docs-content-toc" />
              </div>
            </aside>
            <div className={classNames("docs-content-sidebar-toggle", { "collapse": sidebarCollapse })}
                onClick={onSidebarToggle}>
              <div style={{ position: "relative" }}>
                <List className="feather-list" />
                <X className="feather-x" />
              </div>
            </div>

            <div className="docs-content-inner" onMouseDown={onContentMouseDown}
                onTouchStart={onContentTouchStart}>
              <div className="docs-content-metadata">
                <div className="docs-content-metadata-left">
                  {repository && <div className="docs-content-metadata-repo">{repository}</div>}
                  <div>
                    <a href={`/docs/${currentVersion.version ? `${currentVersion.version}/` : ""}apidocs`}>
                      <Book className="feather" /> API
                    </a>
                  </div>
                  {examples && <div className="docs-content-metadata-examples">{examples}</div>}
                  {edit && <div className="docs-content-metadata-edit">{edit}</div>}
                  <span className="docs-content-metadata-version">
                    <DropDown title={`v${activeVersionTitle}`} align="right">
                      {filterLatestBugfixVersions(sortedAllVersions).map(v => {
                        let md = docsMetadata.find(m => m.version === v)
                        let title = md.metadata.title || v
                        if (latestRelease.version === v) {
                          return (
                            <DropDownItem key={v} active={activeVersion === v}
                                href={`/docs${metadata.href}`}>
                              Latest (v{title})
                            </DropDownItem>
                          )
                        } else {
                          return (
                            <DropDownItem key={v} active={activeVersion === v}
                                href={`/docs/${v}${metadata.href}`}>
                              v{title}
                            </DropDownItem>
                          )
                        }
                      })}
                    </DropDown>
                  </span>
                </div>
                {metadata.label && <div className="docs-content-metadata-label">
                  <Label small nowrap>{metadata.label}</Label>
                </div>}
              </div>
              <div dangerouslySetInnerHTML={{ __html: contents }} ref={contentRef} />
            </div>
          </div>
        </div>
      </div>
      <Footer />
      <style jsx>{styles}</style>
    </main>
  )
}