react-scroll#Element JavaScript Examples

The following examples show how to use react-scroll#Element. 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: index.js    From dnp-website with MIT License 6 votes vote down vote up
render() {
    return (
      <div>
        <Element id={"Selector-title"} />
        <Selector
          handleObjectiveChange={this.handleObjectiveChange}
          objectives={this.props.objectives.objectives}
          currentObjective={this.state.currentObjective}
        />
        <Element id={"Alerts-title"} />
        <AllAlerts
          objectives={this.props.objectives.objectives}
          alerts={this.props.objectives.alerts}
          fyis={this.props.objectives.fyis}
          selectedAlerts={this.state.selectedAlerts}
        />
      </div>
    )
  }
Example #2
Source File: HostEvent.jsx    From club-connect with GNU General Public License v3.0 6 votes vote down vote up
HostEvent = () => (
  <Element name="host-event">
    <section>
      <h2 className={contributeStyles.heading}>
        Host a Workshop or Event with the Club
      </h2>
      <p>
        Events are one of the best parts about being in the club, where
        knowledgeable people in the community can share their knowledge with
        everyone else to benefit the community as a whole. If you have
        experience with a topic, consider hosting an event with the club! You'll
        be able to help fellow club members learn something new, and you'll get
        to practice your public speaking skills! Reach out to us by email
        at&nbsp;
        <a href="mailto:[email protected]">[email protected]</a>
        &nbsp;for more information and to get started.
      </p>
    </section>
  </Element>
)
Example #3
Source File: Volunteer.jsx    From club-connect with GNU General Public License v3.0 6 votes vote down vote up
Volunteer = () => (
  <Element name="volunteer">
    <section className={contributeStyles.volunteer} id="volunteer">
      <h2 className={contributeStyles.heading}>Volunteer for the Club</h2>
      <p>
        As a club, we're tasked with plenty of duties that require a variety of
        skillsets, so even if you aren't interested in programming, you might
        still be able to help us out! Please take a look at our open roles below
        to see which roles would fit you best. Thank you for your interest in
        volunteering for us!
      </p>

      <h3 className={contributeStyles.openRolesHeading}>Open Roles:</h3>

      <UXUIDesignerRole />
      <GraphicDesignerRole />
      <VideoEditorContentCreatorRole />
      <SoftwareEngineerRole />
    </section>
  </Element>
)
Example #4
Source File: Landing.js    From EDAViewer with MIT License 5 votes vote down vote up
export default function Landing(props) {
	const classes = useStyles();

	return (
		<Layout>
			<div id="section-home">
				<Header
					brand={
						<img
							src={logo}
							alt="EDAV"
							width="150"
							height="auto"
						></img>
					}
					fixed
					color="primary"
				/>
				<Parallax id="home-parallax">
					<div className={classes.container}>
						<Grid
							container
							direction="row"
							justify="center"
							alignItems="center"
						>
							<Grid item sm={6} xs={12}>
								<div className={classes.brand}>
									<h1 className={classes.brandTitle}>
										Render EDA Design Files Online
									</h1>
									<h3 className={classes.subtitle}>
										Upload design LEF/DEF files to view in
										your browser.
									</h3>
								</div>
							</Grid>
							<Grid item sm={6} xs={12}>
								<div className={classes.brand}>
									<Upload />
									<div className={classes.exampleDesign}>
										<Button simple href="/example">
											<DeveloperBoardIcon fontSize="large" />{" "}
											Try Example Design
										</Button>
									</div>
								</div>
							</Grid>
						</Grid>
					</div>
				</Parallax>

				<div className={classNames(classes.main, classes.mainRaised)}>
					<Element name="section-features">
						<Features />
					</Element>
					<Element name="section-contact">
						<Contact />
					</Element>
				</div>
				<Footer />
			</div>
		</Layout>
	);
}
Example #5
Source File: about.js    From plataforma-sabia with MIT License 5 votes vote down vote up
Welcome = () => {
	const router = useRouter();
	const { t } = useTranslation(['common', 'pages']);
	const { user } = useAuth();

	const handleIntroButtonClick = (e) => {
		e.preventDefault();
		if (!user?.email) {
			router.push(internalPages.signIn);
		} else {
			toast.info('Iremos te redirecionar para a página principal da plataforma');
			router.push('/');
		}
	};

	useEffect(() => {
		if (isRunningOnBrowser()) {
			const routerHref = router.asPath.split('#')[1];

			if (routerHref) {
				scroller.scrollTo(routerHref, {
					duration: 1,
					offset: -headerHeightInPx,
					smooth: true,
				});
			}
		}
	}, [router.asPath]);

	return (
		<>
			<Head
				title={t('pages:about.title')}
				description={t('pages:about.description')}
				keywords={t('pages:about.keywords')}
			/>
			<Element id="intro" name="intro" className="element">
				<Intro
					title="A vitrine tecnológica mais completa do semiárido"
					subtitle="Um ambiente digital interativo voltado a difundir as tecnologias demandadas e
					ofertadas na resolução de problemas do semiárido brasileiro."
					image={{
						src: '/search-engines-rafiki.svg',
						alt:
							'Mulher de costas segurando uma lupa gigante e olhando para uma barra de busca gigante',
					}}
					button={{
						label: 'Acesse agora',
						handleButtonClick: handleIntroButtonClick,
					}}
				/>
				<About />
			</Element>
			<Element id="features" name="features" className="element">
				<Features />
			</Element>
			{false && (
				<Element id="resources" name="resources" className="element">
					<Resources />
				</Element>
			)}
			<Element id="contact" name="contact" className="element">
				<Contact />
			</Element>
		</>
	);
}
Example #6
Source File: editais.js    From plataforma-sabia with MIT License 5 votes vote down vote up
AnnouncementsPage = ({ initialSearchState, resultsState }) => {
	const { t } = useTranslation(['pages']);

	const [searchState, setSearchState] = useState(initialSearchState);

	const onSearchStateChange = (newSearchState) => {
		searchStateToURL(newSearchState);
		setSearchState(newSearchState);
	};

	return (
		<>
			<Head
				title={t('pages:announcements.title')}
				description={t('pages:announcements.description')}
				keywords={t('pages:announcements.keywords')}
			/>
			<Intro
				title="Encontre editais mais recentes"
				subtitle="Aqui você pode indicar novos editais publicados e realizar buscas nos registros mais atuais de incentivo à pesquisa e inovação. Quem sabe você encontra aquele investimento que precisa, hein?"
				image={{
					src: '/completed-rafiki.svg',
					alt:
						'Ilustração de uma mulher marcando itens como completados em uma folha de papel',
				}}
				link={{
					label: 'Cadastre um edital',
					href: 'register-announcement',
					scroll: true,
				}}
			/>
			<ListItems
				title="Banco de editais"
				searchPlaceholder="Qual edital você busca?"
				createURL={searchStateToURL}
				indexName={algoliaDefaultConfig.announcement.indexName}
				searchState={searchState}
				resultsState={resultsState}
				onSearchStateChange={onSearchStateChange}
				searchComponents={searchComponents}
			/>
			<Element id="register-announcement" name="register-announcement" className="element">
				<RegisterAnnouncement />
			</Element>
		</>
	);
}
Example #7
Source File: ideias.js    From plataforma-sabia with MIT License 5 votes vote down vote up
IdeasBank = ({ initialSearchState, resultsState }) => {
	const [searchState, setSearchState] = useState(initialSearchState);
	const { t } = useTranslation(['pages']);

	const onSearchStateChange = (newSearchState) => {
		searchStateToURL(newSearchState);
		setSearchState(newSearchState);
	};

	return (
		<>
			<Head
				title={t('pages:ideas.title')}
				description={t('pages:ideas.description')}
				keywords={t('pages:ideas.keywords')}
			/>
			<Intro
				title="Não encontrou o que desejava?"
				subtitle="Aqui você pode sugerir novas ideias de desenvolvimento de pesquisa e tecnologia de acordo com sua necessidade e para resolver problemas comuns do nosso povo. Veja abaixo as ideias já sugeridas pelos nossos usuários e registre a sua também."
				image={{
					src: '/brainstorming-rafiki.svg',
					alt:
						'Pessoas fazendo um brainstorming com uma ilustração de uma grande lâmpada acima delas',
				}}
				link={{
					label: 'Cadastre sua ideia',
					href: 'register-idea',
					scroll: true,
				}}
			/>
			<ListItems
				title="Banco de ideias"
				searchPlaceholder="Qual ideia você busca?"
				createURL={searchStateToURL}
				indexName={algoliaDefaultConfig.idea.indexName}
				searchState={searchState}
				resultsState={resultsState}
				onSearchStateChange={onSearchStateChange}
				searchComponents={searchComponents}
			/>
			<Element id="register-idea" name="register-idea" className="element">
				<RegisterIdea />
			</Element>
		</>
	);
}
Example #8
Source File: index.js    From circles-website with GNU Affero General Public License v3.0 4 votes vote down vote up
function FAQ({ t }) {
  const [inputValue, setInputValue] = useState('');
  const { pathname, query, push } = useRouter();
  const inputRef = useRef();

  const selectedIndex = query.open && Number(query.open);
  const items = t('items', { returnObjects: true });

  useEffect(() => {
    if (selectedIndex) {
      const openItem = items && items[selectedIndex];
      scroller.scrollTo(openItem.question, {
        duration: 300,
        smooth: true,
        offset: -90,
      });
    }
  }, [selectedIndex]);

  useEffect(() => {
    if (!selectedIndex) {
      scroller.scrollTo('page-wrapper', {
        duration: 200,
        smooth: true,
      });
      inputRef.current.focus();
    }
  }, []);

  const getPathnameWithLang = () => {
    let pathnameWithLang = pathname;
    if (i18n.language !== 'en') {
      pathnameWithLang = '/' + i18n.language + pathname;
    }
    return pathnameWithLang;
  };

  const setSelectedIndex = (itemIndex) => {
    const pathnameWithLang = getPathnameWithLang();
    push(
      { pathname: pathnameWithLang, query: { open: itemIndex } },
      undefined,
      {
        shallow: true,
        scroll: false,
      }
    );
    setInputValue('');
  };

  let suggestions = [];
  if (inputValue.length > 3) {
    items.forEach((item, index) => {
      if (item.question.toLowerCase().includes(inputValue.toLowerCase())) {
        suggestions.push({
          label: (
            <ScrollLink
              onClick={() => setSelectedIndex(index)}
              to={item.question}
              smooth
              duration={300}
              offset={-80}
            >
              <Box pad="small">{item.question}</Box>
            </ScrollLink>
          ),
          value: item.question,
          topic: item.topic,
          index,
        });
      }
    });
  }

  const onSelect = (event) => {
    const suggestion = event.suggestion;
    setSelectedIndex(suggestion.index);
    setInputValue('');
    scroller.scrollTo(suggestion.question, {
      duration: 300,
      offset: -80,
      smooth: true,
    });
  };

  const handlePanelSelect = (index) => {
    if (index === selectedIndex) {
      setSelectedIndex(null);
      return;
    }
    setSelectedIndex(index);
  };

  const onFilterFocus = () => {
    const pathnameWithLang = getPathnameWithLang();
    push({ pathname: pathnameWithLang, query: {} }, undefined, {
      shallow: true,
      scroll: false,
    });
  };

  const isClient = typeof window !== 'undefined';

  return (
    <div className="page">
      <Head>
        <title>Circles UBI | Frequently Asked Questions</title>
        <link
          rel="stylesheet"
          type="text/css"
          charSet="UTF-8"
          href="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.6.0/slick.min.css"
        />
        <link
          rel="stylesheet"
          type="text/css"
          href="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.6.0/slick-theme.min.css"
        />
        <meta
          name="description"
          content="Circles | A Basic Income in the Blockchain"
        />
        <meta
          name="keywords"
          content="circles, circle, gift economy, cryptocurrency, universal basic income, blockchain,
          ubi, money, berlin, andrew yang, bernie sanders, doughnut economics, degrowth, local community currency, p2p, commons, democratic money,
          faircoin, trustlines, gnosis"
        />
      </Head>

      <Layout>
        {(large) => (
          <Box
            width="large"
            alignSelf="center"
            className="page-wrapper"
            elevation="medium"
          >
            <Element name="page-wrapper">
              <Box width="100%" pad={{ top: 'large' }}>
                <Box alignSelf="center">
                  <Image
                    src="/images/logo.svg"
                    margin={{ bottom: 'large' }}
                    style={{ width: 90, height: 90 }}
                  />
                </Box>
                <Box
                  margin={{ left: 'medium', right: 'medium' }}
                  width="medium"
                  alignSelf="center"
                >
                  <Text
                    weight="bold"
                    size="large"
                    textAlign="center"
                    margin={{ bottom: 'medium' }}
                  >
                    {t('title')}
                  </Text>
                  <Box pad={{ horizontal: 'medium' }}>
                    <Grommet theme={textFieldTheme}>
                      <FormField>
                        <TextInput
                          ref={inputRef}
                          value={inputValue}
                          suggestions={suggestions}
                          placeholder={t('placeholder')}
                          type="search"
                          dropProps={{ pad: { horizontal: 'medium' } }}
                          onChange={(event) => {
                            setInputValue(event.target.value);
                          }}
                          onFocus={onFilterFocus}
                          onSelect={onSelect}
                        />
                      </FormField>
                    </Grommet>
                  </Box>
                </Box>
              </Box>
            </Element>
            <Row>
              <Box width="100%">
                <Box pad={{ top: 'large', horizontal: 'large' }}>
                  <Text
                    margin={{ top: 'medium', bottom: 'medium' }}
                    size="large"
                  >
                    Video Library
                  </Text>
                  <VideoSlider large={large} />
                </Box>

                <Box
                  margin={{ left: 'medium', right: 'medium' }}
                  width="large"
                  pad={{ horizontal: 'large', bottom: 'large' }}
                  alignSelf="center"
                >
                  {isClient && (
                    <Accordion activeIndex={selectedIndex} animate={false}>
                      {items.map((item, index) => (
                        <Box key={item.question}>
                          {(index === 0 ||
                            item.topic !== items[index - 1].topic) && (
                            <Text
                              margin={{ top: 'xlarge', bottom: 'medium' }}
                              size="large"
                            >
                              {item.topic}
                            </Text>
                          )}
                          <AccordionPanel
                            id={item.question}
                            header={
                              <Box
                                direction="row"
                                pad="medium"
                                onClick={() => handlePanelSelect(index)}
                              >
                                <Element
                                  name={item.question}
                                  style={{ flexBasis: '100%', flexGrow: 3 }}
                                >
                                  <Text weight="bold">{item.question}</Text>
                                </Element>
                                {index === selectedIndex ? <Up /> : <Down />}
                              </Box>
                            }
                          >
                            <Box
                              pad={{
                                horizontal: 'medium',
                                bottom: 'medium',
                              }}
                              animation="fadeIn"
                            >
                              {item.answer.map((paragraph) => (
                                <Paragraph
                                  key={paragraph && paragraph.substring(0, 20)}
                                  className="faq-answer-anchor"
                                  size="small"
                                >
                                  {renderHTML(paragraph)}
                                </Paragraph>
                              ))}
                            </Box>
                          </AccordionPanel>
                        </Box>
                      ))}
                    </Accordion>
                  )}
                </Box>
              </Box>
            </Row>
          </Box>
        )}
      </Layout>
    </div>
  );
}
Example #9
Source File: index.js    From dnp-website with MIT License 4 votes vote down vote up
render() {
    const descriptions = this.props.datasetInfo.Description || []
    const compositions = this.props.datasetInfo.Composition || []
    const provenances = this.props.datasetInfo.Provenance || []
    const collections = this.props.datasetInfo.Collection || []
    const managements = this.props.datasetInfo.Management || []

    return (
      <>
        <ScrollButton />
        <div className={styles.flexbox}>
          <h3 className={styles.datasetTitle}>Dataset Information</h3>
          <span className={styles.datasetUnderlineBold}></span>
        </div>
        <Element id={"Description-title"}> </Element>
        <p className={styles.datasetParagraph}>
          Information about the ongoing management of the dataset, such as how
          the data will be maintained, updated, and the best contact for
          further inquiries.
        </p>
        <p className={styles.datasetParagraph}>
          The categories and questions that comprise this section are drawn from 
          the terrific work of many teams; we have drawn heavily on the work of{' '}
          <a href="https://arxiv.org/abs/1803.09010" target="_blank" rel="noreferrer">
            Datasheets for Datasets <FontAwesomeIcon icon={faExternalLinkAlt} />
          </a>, 
          and supplemented that with work by{' '}
          <a href="https://ai-global.org/" target="_blank" rel="noreferrer">
            AI Global <FontAwesomeIcon icon={faExternalLinkAlt} />
          </a>,{' '}
          <a href="http://data.world/" target="_blank" rel="noreferrer">
            data.world <FontAwesomeIcon icon={faExternalLinkAlt} />
          </a>, and{' '}
          <a href="https://deon.drivendata.org/" target="_blank" rel="noreferrer">
            DrivenData <FontAwesomeIcon icon={faExternalLinkAlt} />
          </a>. 
          We further refined this section based on feedback from colleagues at the 
          Department of Education, AI Global, and Memorial Sloan Kettering.
        </p>

        <span className={styles.datasetUnderlineBold}></span>

        <h3 className={styles.datasetSubHeader}>Description</h3>
        <ol className={styles.datasetOl}>
          {descriptions.map(description => (
            <li className={styles.datasetLi}>
              {description.question}
              {description.type === "markdown" ? (
                <ReactMarkdown
                  className={styles.datasetMrkdwn}
                  children={description.answer || description.content}
                />
              ) : (
                ""
              )}
            </li>
          ))}
        </ol>

        <Element id={"Composition-title"}> </Element>
        <span className={styles.datasetUnderline}></span>

        <h3 className={styles.datasetSubHeader}>Composition</h3>

        <ol className={styles.datasetOl}>
          {compositions.map(composition => (
            <li className={styles.datasetLi}>
              {composition.question}
              {composition.type === "markdown" ? (
                <ReactMarkdown
                  className={styles.datasetMrkdwn}
                  children={composition.answer || composition.content}
                />
              ) : (
                ""
              )}
            </li>
          ))}
        </ol>
        <Element id={"Provenance-title"}> </Element>
        <span className={styles.datasetUnderline}></span>

        <h3 className={styles.datasetSubHeader}>Provenance</h3>

        <ol className={styles.datasetOl}>
          {provenances.map(provenance => (
            <li className={styles.datasetLi}>
              {provenance.question}
              {provenance.type === "markdown" ? (
                <ReactMarkdown
                  className={styles.datasetMrkdwn}
                  children={provenance.answer || provenance.content}
                />
              ) : (
                ""
              )}
            </li>
          ))}
        </ol>
        <Element id={"Collection-title"}></Element>
        <span className={styles.datasetUnderline}></span>

        <h3 className={styles.datasetSubHeader}>Collection</h3>

        <ol className={styles.datasetOl}>
          {collections.map(collection => (
            <li className={styles.datasetLi}>
              {collection.question}
              {collection.type === "markdown" ? (
                <ReactMarkdown
                  className={styles.datasetMrkdwn}
                  children={collection.answer || collection.content}
                />
              ) : (
                ""
              )}
            </li>
          ))}
        </ol>

        <Element id={"Management-title"}> </Element>
        <span className={styles.datasetUnderline}></span>
        <h3 className={styles.datasetSubHeader}>Management</h3>

        <ol className={styles.datasetOl}>
          {managements.map(management => (
            <li className={styles.datasetLi}>
              {management.question}
              {management.type === "markdown" ? (
                <ReactMarkdown
                  className={styles.datasetMrkdwn}
                  children={management.answer || management.content}
                />
              ) : (
                ""
              )}
            </li>
          ))}
        </ol>
        <span className={styles.datasetUnderline}></span>
      </>
    )
  }
Example #10
Source File: OpenSource.jsx    From club-connect with GNU General Public License v3.0 4 votes vote down vote up
OpenSource = () => (
  <Element name="open-source" className="contribute-open-source">
    <section className={contributeStyles.openSourceSoftware} id="open-source">
      <h2 className={contributeStyles.heading}>
        Contribute to the Club's Open Source Software
      </h2>

      <p>
        Most of the club's software is open source on our&nbsp;
        <a
          href="https://github.com/bc-compsci-club"
          target="_blank"
          rel="noopener noreferrer"
        >
          GitHub Organization
        </a>
        ! We're currently looking for contributors interested in helping us out
        with some of our software, such as new features and bug fixes for the
        club's website.
      </p>

      <h3>Here's how you can help:</h3>
      <ol className={contributeStyles.openSourceSteps}>
        <div className={contributeStyles.openSourceFirst}>
          <h4>First, claim the issue that you want to work on.</h4>
          <li>
            Pick a project you want to contribute to and visit it's GitHub
            repository.
          </li>

          <Alert variant="primary">
            Our open source projects can be found on our&nbsp;
            <Alert.Link
              href="https://github.com/bc-compsci-club"
              target="_blank"
              rel="noopener noreferrer"
            >
              GitHub Organization
            </Alert.Link>
            .
          </Alert>

          <li>
            Go to the Issues tab and choose an open issue you want to help with.
          </li>
          <li>
            Leave a comment in the issue stating you would like to work on it.
          </li>
          <Alert variant="info">
            If an issue is already being worked on, consider reaching out to the
            person or group working on it and ask if you can help! Multiple
            contributors can receive credit when you co-author commits.
          </Alert>
        </div>
        <div>
          <h4>
            Once you've been assigned the issue by a maintainer, you can fork
            the project repository and stat working on it!
          </h4>
          <li>Fork the project's repository to your own GitHub account.</li>
          <li>Clone the forked repository to your computer.</li>
          <li>
            Create a branch in your forked repository on your computer for your
            changes.
          </li>
          <li>
            Make your changes and commit them to your new branch on your
            computer.
          </li>
          <Alert variant="success">
            Be sure to leave comments about your progress and status on the
            issue every few days while you're working on it! If you're stuck
            with something or need some more information, it's okay to ask for
            help or for clarification about the issue!
          </Alert>
          <Alert variant="warning">
            The reason we ask that you provide updates about your issue every
            few days is to make sure that claimed issues are actively being
            worked on. If you do not provide any updates within a week, your
            issue may be unassigned from you and released for others to claim.
            There are other contributors that may have wanted to take the issue
            you claimed!
          </Alert>
        </div>
        <div>
          <h4>
            Finally, once you're done, you can submit a pull request to merge
            your changes with the main branch!
          </h4>
          <li>Push your changes to your fork on GitHub.</li>
          <li>Open a pull request for your changes.</li>
        </div>
      </ol>
      <p>
        After you submit your pull request, a maintainer (or multiple!) will
        review your code. If all goes well and your pull request is accepted,
        your changes will be merged into the main branch!
      </p>
      <p>
        If things don't go as smoothly as they should, don't worry! Code
        reviewers will usually provide guidance about how you could fix any
        problems with your submission. Don't take it personally, as the
        maintainers are just trying their best to maintain the quality of the
        codebase. Don't be afraid to ask for help on the pull request if needed!
      </p>
      <a
        href="https://opensource.guide/how-to-contribute/#opening-a-pull-request"
        target="_blank"
        rel="noopener noreferrer"
      >
        Need more help? Here's a nice tutorial on how to contribute to an open
        source project!
      </a>
    </section>
  </Element>
)
Example #11
Source File: CellSetsTool.jsx    From ui with MIT License 4 votes vote down vote up
CellSetsTool = (props) => {
  const { experimentId, width, height } = props;

  const dispatch = useDispatch();
  const cellSets = useSelector(getCellSets());
  const {
    loading, error, hierarchy, properties, hidden, selected: allSelected,
  } = cellSets;

  const genes = useSelector(
    (state) => state.genes,
  );
  const filteredCells = useRef(new Set());

  const [activeTab, setActiveTab] = useState('cellSets');

  useEffect(() => {
    filteredCells.current = generateFilteredCellIndices(genes.expression.data);
  }, [genes.expression.data]);

  useEffect(() => {
    // load the expression data for an arbitrary gene so that we can determine
    // which cells are filtered
    const [gene] = Object.keys(genes.properties.data);
    if (Object.is(gene, undefined)) return;

    dispatch(loadGeneExpression(experimentId, [gene], 'CellSetsTool'));
  }, [genes.properties.data]);

  const FOCUS_TYPE = 'cellSets';

  useEffect(() => {
    dispatch(loadCellSets(experimentId));
  }, []);

  const [cellSetTreeData, setCellSetTreeData] = useState(null);
  const [metadataTreeData, setMetadataTreeData] = useState(null);

  useEffect(() => {
    setCellSetTreeData(composeTree(hierarchy, properties, 'cellSets'));
    setMetadataTreeData(composeTree(hierarchy, properties, 'metadataCategorical'));
  }, [hierarchy, properties]);

  const [numSelected, setNumSelected] = useState(0);

  useEffect(() => {
    const selected = allSelected[activeTab];
    const selectedCells = union(selected, properties);

    const numSelectedUnfiltered = new Set([...selectedCells]
      .filter((cellIndex) => !filteredCells.current.has(cellIndex)));
    setNumSelected(numSelectedUnfiltered.size);
  }, [activeTab, allSelected, properties]);

  const onNodeUpdate = useCallback((key, data) => {
    dispatch(updateCellSetProperty(experimentId, key, data));
  }, [experimentId]);

  const onNodeDelete = useCallback((key) => {
    dispatch(deleteCellSet(experimentId, key));
  }, [experimentId]);

  const onCellSetReorder = useCallback((cellSetKey, newPosition) => {
    dispatch(reorderCellSet(experimentId, cellSetKey, newPosition));
  }, [experimentId]);

  const onCheck = useCallback((keys) => {
    dispatch(updateCellSetSelected(keys, activeTab));
  }, [experimentId, activeTab]);

  /**
   * Renders the content inside the tool. Can be a skeleton during loading
   * or a hierarchical tree listing all cell sets.
   */
  const renderContent = () => {
    let operations = null;
    const selected = allSelected[activeTab];

    if (numSelected) {
      operations = (
        <Space>
          <CellSetOperation
            icon={<MergeCellsOutlined />}
            onCreate={(name, color) => {
              dispatch(createCellSet(experimentId, name, color, union(selected, properties)));
            }}
            ariaLabel='Union of selected'
            helpTitle='Create new cell set by combining selected sets in the current tab.'
          />
          <CellSetOperation
            icon={<BlockOutlined />}
            onCreate={(name, color) => {
              dispatch(
                createCellSet(experimentId, name, color, intersection(selected, properties)),
              );
            }}
            ariaLabel='Intersection of selected'
            helpTitle='Create new cell set from intersection of selected sets in the current tab.'
          />
          <CellSetOperation
            icon={<SplitCellsOutlined />}
            onCreate={(name, color) => {
              dispatch(createCellSet(experimentId, name, color, complement(selected, properties)));
            }}
            ariaLabel='Complement of selected'
            helpTitle='Create new cell set from the complement of the selected sets in the current tab.'
          />
          <Text type='primary' id='selectedCellSets'>
            {`${numSelected} cell${numSelected === 1 ? '' : 's'} selected`}
            {activeTab === 'metadataCategorical'}
          </Text>
        </Space>
      );
    }

    return (
      <>
        <Tabs
          size='small'
          activeKey={activeTab}
          onChange={(key) => setActiveTab(key)}
          tabBarExtraContent={operations}
        >
          <TabPane tab='Cell sets' key='cellSets'>
            <HierarchicalTree
              experimentId={experimentId}
              treeData={cellSetTreeData}
              store={FOCUS_TYPE}
              onCheck={onCheck}
              onNodeUpdate={onNodeUpdate}
              onNodeDelete={onNodeDelete}
              onCellSetReorder={onCellSetReorder}
              showHideButton
              checkedKeys={selected}
            />
          </TabPane>
          <TabPane tab='Metadata' key='metadataCategorical'>
            {metadataTreeData?.length > 0 ? (
              <HierarchicalTree
                experimentId={experimentId}
                treeData={metadataTreeData}
                store={FOCUS_TYPE}
                onCheck={onCheck}
                onNodeUpdate={onNodeUpdate}
                onNodeDelete={onNodeDelete}
                onCellSetReorder={onCellSetReorder}
                showHideButton
                checkedKeys={selected}
              />
            )
              : (
                <Empty description={(
                  <>
                    <Text type='primary'>You don&apos;t have any metadata added yet.</Text>
                    <Text type='secondary'>Metadata is an experimental feature for certain pre-processed or multi-sample data sets.</Text>
                  </>
                )}
                />
              )}
          </TabPane>

        </Tabs>
      </>
    );
  };

  if (loading) return <Skeleton active={false} title={false} />;
  if (!cellSetTreeData || !metadataTreeData) return <Skeleton active title={false} avatar />;

  if (error) {
    return (
      <PlatformError
        error={error}
        onClick={() => dispatch(loadCellSets(experimentId))}
      />
    );
  }

  return (
    <Element
      className='element'
      id='cell-set-tool-container'
      style={{
        position: 'relative',
        height: `${height - 40}px`,
        width: `${width - 8}px`,
        overflow: 'auto',
        paddingLeft: '5px',
        paddingRight: '5px',
      }}
    >
      <Space direction='vertical'>
        {hidden.size > 0 ? (
          <Alert
            message={`${hidden.size} cell set${hidden.size > 1 ? 's are' : ' is'} currently hidden.`}
            type='warning'
            action={<Button type='link' size='small' onClick={() => dispatch(unhideAllCellSets(experimentId))}>Unhide all</Button>}
          />
        ) : (<></>)}
        {renderContent()}
      </Space>
    </Element>
  );
}