npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@react-lit/descendants

v0.7.0

Published

A descendant index solution for better accessibility support in compound components.

Downloads

16

Readme

@react-lit/descendants

A descendant index solution for better accessibility support in compound components.

This package provides these key tools:

  • createDescendantContext: Creates a special context object to deal with registering descendants in a tree.
  • useDescendantsInit: A hook to create a state object containing a descendants array and setter function.
  • DescendantProvider: A provider that accepts the descendants array, the state setter, and the component's context object for use at the top of the component tree.
  • useDescendant: A hook called in the body of a nested descendant component that registers its DOM node and returns its index relative to other descendants in the tree.
  • useDescendants: A hook that accepts the descendant context and returns descendants registered to the passed context.

Installation

$ npm i @react-lit/descendants
# or
$ yarn add @react-lit/descendants

Example

import * as React from 'react';
import {
  createDescendantContext,
  DescendantProvider,
  useDescendant,
  useDescendantsInit,
} from "@react-lit/descendants";

const DescendantContext = createDescendantContext("DescendantContext");
const MenuContext = React.createContext();

function Menu({ id, children }) {
  // NOTE(joel): We could be less explicit here and set this up in the
  // `DescendantProvider`, but you may want to do something with `descendants`
  // in your top-level component and we don't want to force creating an
  // arbitrary child component just so we can consume the context.
  const [descendants, setDescendants] = useDescendantsInit();
  const [activeIndex, setActiveIndex] = React.useState(-1);
  return (
    <DescendantProvider
      context={DescendantContext}
      items={descendants}
      set={setDescendants}
    >
      <MenuContext.Provider
        value={{ buttonId: `button`, activeIndex, setActiveIndex }}
      >
        {children}
      </MenuContext.Provider>
    </DescendantProvider>
  );
}

function MenuList(props) {
  const { buttonId, activeIndex } = React.useContext(MenuContext);
  return (
    <div
      role="menu"
      aria-labelledby={buttonId}
      aria-activedescendant={activeIndex}
      tabIndex={-1}
    >
      {children}
    </div>
  );
}

function MenuItem({ index: explicitIndex, ...props }) {
  const { activeIndex, setActiveIndex } = React.useContext(MenuContext);
  const ref = React.useRef(null);

  // NOTE(joel): We use a stateful ref here because we need the actual DOM
  // element for our descendant object, but also need to update state after
  // the dom ref is placed.
  const [element, elementSet] = React.useState(null);
  const handleRefSet = React.useCallback((refValue) => {
    ref.current = refValue;
    elementSet(refValue);
  }, []);

  // NOTE(joel): The descendant should be memoized to prevent endless render
  // loops after the collection state is updated.
  const descendant = React.useMemo(() => {
    return {
      element,
      // NOTE(joel): You can pass arbitrary data into a descendant object which
      // can come in handy for features like typeahead!
      key: props.label,
    };
  }, [element, props.label]);

  // NOTE(joel): Tell the `useDescendant` hook to use a specific context.
  // This is key in case you have a compound component that needs index
  // tracking in separate correlating descendant components (like `Tabs`)
  // If you want to declare a specific index value, you can pass it as the
  // third argument here. This is almost never needed but we provide it as an
  // escape hatch for special circumstances.
  const index = useDescendant(descendant, DescendantContext, explicitIndex);

  // NOTE(joel): After we know the index, we can use it!
  const isSelected = index === activeIndex;
  function select() {
    if (!isSelected) {
      setActiveIndex(index);
    }
  }

  return (
    <div
      role="menuitem"
      ref={handleRefSet}
      data-selected={isSelected ? "" : undefined}
      tabIndex={-1}
      onMouseEnter={select}
      {...props}
    />
  );
}

Development

(1) Install dependencies

$ npm i
# or
$ yarn

(2) Run initial validation

$ ./Taskfile.sh validate

(3) Run tests in watch-mode to validate functionality.

$ ./Taskfile test -w

This project was set up by @jvdx/core