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

@martel/haunted-router

v0.4.1

Published

A client-side router for Haunted

Downloads

8

Readme

Haunted Router for Haunted 🦇 🎃

npm npm bundle size Build Status

A client-side router for Haunted.

Table of contents

Getting started

Installing

Using npm:

npm install --save haunted-router

Importing

If using a bundler, Haunted Router can be imported like any other library:

import { useRoutes, useTitle, navigateTo, replaceTo } from 'haunted-router';

Haunted Router can also work directly in the browser without using any build tool. Here's an example with unpkg:

import { useRoutes, useTitle, navigateTo, replaceTo } from 'https://unpkg.com/haunted-router?module';

Usage

Define the routes

Routes are defined using the useRoutes hook. It takes an object as parameter, along with a fallback value.

The keys of the object are the paths to be matched, while the values are functions to be executed when the paths are matched. The value returned by the hook is the same as the one returned by the function.

Example:

import { useRoutes } from 'haunted-router';
// Where using lit-html as an example, but any rendering library compatible with haunted will do
import { html, nothing } from 'lit-html';

function App() {
  const routeResult = useRoutes({
    '/': () => html`<x-page-index></x-page-index>`,
    '/about': () => html`<x-page-about></x-page-about>`,
  }, nothing);

  return html`
    <main>
      ${routeResult.outlet}
    </main>
  `;
}

Use the URL parameters

You can get the parameters in of the URL using the useSearchParams hook. This hook takes no parameter, and returns an object containing the search parameters in the search string. All values are strings. Use object decomposition to assign default values.

Example:

function MyShoppingPage() {
  const {
    minPrice = 0,
    maxPrice = Infinity,
  } = useSearchParams();

  return html`
    Active filters:
    <ul>
      ${minPrice != 0
        ? html`
            <li>Minimum price: ${minPrice}</li>
          `
        : ''}
      ${maxPrixe !== Infinity
        ? html`
            <li>Maximum price: ${maxPrice}</li>
          `
        : ''}
    </ul>
  `;
}

Define the title

The title of the document can be modified using the useTitle hook. It's only parameter is a string, and it doesn't return anything. The title is brought back to the original when the component is disconnected from the DOM.

Example:

function App() {
  useTitle('My awesome app !');

  return html`
    <header>
      <h1>My awesome app !</h1>
    </header>
  `;
}

Navigate

There are two ways to navigate using haunted-router:

  • Programatically, using navigateTo(url, state) or replaceTo(url, state). navigateTo creates an entry in the history, whilst replaceTo doesn't.
  • With anchors, using the router-link custom element. Add the replace attribute to prevent creating an history entry.

Example:

import { navigateTo, replaceTo } from 'haunted-router';

navigateTo('/url/to/destination', { foo: 'bar' });
replaceTo('/url/to/second-tab');

// Or
import { html } from 'lit-html';

return html`
  <a is="router-link" href="/url/to/destination" .state=${{ foo: 'bar' }}></a>
  <a is="router-link" href="/url/to/second-tab" replace></a>
`;

state can be of any type and is optional.

⚠️ As custom built-in elements have poor browser support, the router-link custom built-in element is not automatically loaded. The behavior can however be polyfilled (see Browser Support). To use it, import haunted-router/lib/router-link.js once.

Redirect

You can use the router-redirect custom element to redirect the user to a URL. Add the replace attribute to prevent creating an entry in the history.

Example:

import { html } from 'lit-html';

return html`
  <router-redirect url="/url/to/destination" />
  <router-redirect url="/new/replaced/url" replace />
`;

⚠️ The router-redirect custom element is optional. To use it, import haunted-router/lib/router-redirect.js once.

Complete example

main.js

import { component } from 'haunted';
import { useRoutes, useTitle } from 'haunted-router';

// A reminder that all libraries supported by haunted can be used
import { html, nothing } from 'lit-html';

import { mainRoutes } from './router.js';

const MyApp = () => {
  const routeResult = useRoutes(mainRoutes, nothing);

  return html`
    <header>My Awesome App</header>
    <main>${routeResult.outlet}</main>
    <footer>&copy; Haunted Router</footer>
  `;
};

router.js

const mainRoutes = {
  // A plain url
  // Every top level URL must begin with a slash
  '/': () => html`<x-page-home></x-page-home>`,

  // An URL with parameters
  '/product/:id': ({ id }) => html`<x-page-product .id=${id}></x-page-product>`,

  // Dynamically import the component
  '/about': () => {
    // No need to wait for the result, the component will appear once loaded
    import('./about.js');
    return html`<x-page-about></x-page-about>`;
  },

  // Putting a star at the end will match all the URLs that starts with the string
  // It can be used to match subroutes
  '/account*': () => html`<x-page-account></x-page-account>`,
};

const accountRoutes = {
  '/detail': () => html`<x-tab-detail></x-tab-detail>`,
  '/password': () => html`<x-tab-password></x-tab-password>`,
};

export { mainRoutes, accountRoutes };

account.js

import { component } from 'haunted';
import { useRoutes } from 'haunted-router';
import { html, nothing } from 'lit-html';

import { accountRoutes } from './router.js';

const PageAccount = () => {
  useTitle('My Account');

  const tabResult = useRoutes(accountRoutes, nothing);

  return html`
    <h1>Account</h1>
    ${tabResult.outlet}
  `;
};

customElements.define('x-page-account', component(PageAccount));

Browser support

See Haunted's browser support for required polyfills in the README of the repository.

Haunted Router supports all browsers that support custom built-in elements.

⚠️ Safari and Edge <= 18 do not support these, but the behavior can be polyfilled, for example, using the lightweight @ungap/custom-elements-builtin.

If you choose not to use the router-link custom built-in element, the browser support is the same as Haunted.

Insight

The router merely executes the function that corresponds to the current route, and returns the result. It can be used to execute anything you want when the user navigates.