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

@stack-spot/portal-layout

v2.20.0

Published

Implements the general layout for a Stackspot web application.

Downloads

2,464

Readme

Layout

Implements the general layout for a Stackspot web application.

Components

  • Header: a top bar with a logo and a user menu.
  • Menu (sections): a left vertical bar with every section in the application.
  • Menu (content, or contextual): a left vertical bar on the right of the menu sections that shows the menu for the current section or page.
  • Page: the actual page content.
  • Modal: a floating panel to show modal content.
  • Right panel: a floating panel that shows like a modal, but is fixed to the right side of the window.
  • Bottom dialog: a floating fixed panel fixed to the bottom of the window.
  • Toaster: a floating set of cards at the top right corner used to show notifications.
  • Tour: component associated floating cards used to present new features to users.

The overlay components (modal, right panel, bottom dialog and toaster) are controlled imperatively through the overlay object. Example: overlay.showModal(options).

The header, menus and page are rendered by the component <Layout> according to its props. If you need a custom header or menu, you can use <RawLayout> which accepts React elements instead of configuration objects.

You can also render the menu and header separately by importing the components <MenuContent>, <MenuSections> or <Header>.

The Layout component

To render the layout, use the component <Layout>, check the example below:

const menu = {
  options: [
    {
      label: 'Section 1',
      icon: <MyIcon />,
      href: '/section-1',
      active: true,
    },
    {
      label: 'Section 2',
      icon: <MyIcon2 />,
      href: '/section-2',
    }
  ]
}

const header = {
  userName: 'Test',
  email: '[email protected]',
  center: <SearchField />,
  options: [
    {
      label: 'Logout',
      icon: <Logout />,
      onClick: () => logout(),
    }
  ]
}

const MyApp = () => {
  return <Layout menu={menu} header={header}>Hello World!</Layout>
}

The Header and Menu in a layout are highly customizable. Check the code documentation for more details!

Modal and right panel pitfalls

Both these elements of the layout were designed to be used as imperative/procedural calls, i.e. instead of rendering a modal based on a React State, we just call overlay.showModal(content) or await confirm({ message: '...' }). This makes using a modal much easier, but prevents us from using React Portals.

Without the use of React Portals, the modal content becomes isolated from the context it was called, meaning, updating a state of the component who called it, won't update the content of the modal.

Programming a modal isolated from whatever called it is a good thing! It makes the code more maintainable and less coupled. But we need to keep this behavior in mind. See the example below:

const Counter = () => {
  const [current, setCurrent] = useState(0)
  function showCounterModal() {
    overlay.showModal({
      title: 'Counter',
      children: (
        <div>
          <p>The value is {counter}.</p>
          <button onClick={setCurrent(v => v + 1)}>Increment</button>
        </div>
      ),
    })
  }
  return <button onClick={showCounterModal}>Show counter modal</button>
}

This won't work because the state was declared outside the modal. We may click as muck as we want to increment the value, the text will keep saying the first value it showed. The value will only be updated once we close the modal and open it up again.

The correct way of doing this would be, instead of declaring the state outside the modal, declaring it inside, see the example below:

const CounterModal = () => {
  const [current, setCurrent] = useState(0)

  return (
    <div>
      <p>The value is {counter}.</p>
      <button onClick={setCurrent(v => v + 1)}>Increment</button>
    </div>
  )
}

const Counter = () => {
  function showCounterModal() {
    overlay.showModal({
      title: 'Counter',
      children: <CounterModal />,
    })
  }
  return <button onClick={showCounterModal}>Show counter modal</button>
}

This is going to work as expected, because the modal controls its own state!

If you really need to use an external state, consider:

  1. Using a React Context declared before <Layout>.
  2. Calling overlay.showModal() again to force its UI to update with the new state value.

Attention: opening and closing modals are side-effects!

The operations to open or close a modal or right panel are side effects, and, because of this, they should not be called during the rendering phase of a component!

You should open/close a modal/rightPanel on events (e.g. onClick) or React effects (e.g. useEffect).

Layout elements

You can easily refer to layout elements by using their ids: elementIds or by calling the function getLayoutElements().

Error handling

Every part of the layout is wrapped under a React Error Boundary. If an error occurs in a part of the layout that is large enough (e.g. Page), an error feedback is rendered. Otherwise, the content stays empty. The error is always logged to the console.

In development mode, a button shows up in the error feedback, the button allows the error message to be seen without opening the console. For the error boundaries that don't show an error feedback, in development mode, a small error icon is rendered.

To better format errors in an error boundary, please pass errorDescriptor to the component <Layout>, this is a function that transforms an Error into something readable to present to the user.

To intercept every error when it's catch by an Error Boundary, use the property onError of the component <Layout>. This is useful for sending error reports to the backend.