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

@locmod/modal

v3.0.1

Published

Abstract modal

Downloads

140

Readme

@locmod/modal

Installation

npm install --save @locmod/modal

Structure

Modal system consists of:

  • manager (controls the state)
  • and public methods and types:

| | | |------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | global type ModalsRegistry | Provides modal props mapping for all other methods: { [modalName: string]: {...} } | | global type ExtendModalsRegistry | Type helper. Use in modal cmponents to add mapping of modal name & props to ModalsRegistry. See Typecheking section for details | | type ModalName | Registered modal names keyof ModalsRegistry | | type ModalComponentProps<YourModalProps> | helper type to use for modals, combine your business-logic props with props provided by ModalRenderer / standaloneModal. See ModalComponentProps section for details. | | type ModalComponent<YourModalProps> | helper type to use for modals, combine your business-logic props with ModalComponentProps | | openModal(name: ModalName) | Opens a modal by name | | closeModal(name: ModalName) | Closes all modals by name | | registerModals(registry: Record<string, ModalComponent>) | Registers modal to render by ModalRenderer. See Registry section for details | | getOpenModalNames() | Returns an array of open modal names | | <ModalsRenderer /> | Handles render of all modals which added by registerModals, should be added to App render | | standaloneModal(string: ModalName, ModalComponent) | HOC. It handles render of wrapped modal. Should be added to render manually. Use for specific modals, e.g. a modal requires some non-global Context that isn't accessible from ModalsRenderer |

Difference of standalone modals

  • ModalsRenderer skips (doesn't render) modals which are wrapped by this HOC
  • so you don't need to register it by registerModals
  • you must put this modal in render manually

Use for specific modals, e.g. a modal requires some non-global Context that isn't accessible from ModalsRenderer

Examples

See live examples on codesandbox.io:

Manager

Controls modals system state, provides event listeners and emit events. Controls modals registry.

Only some methods of the manager are public.

How to

ModalComponentProps

When you build your modal, even if you don't need business logic props, you have additional props which are provided by ModalRenderer / standaloneModal - use type ModalComponentProps:

import { type ModalComponentProps } from '@locmod/modal'

/*
*  type ModalComponentProps = {
*    name: string
*    closeModal: (withOnClose?: boolean) => void
*    onClose?: () => void
*  }
*/

const ExampleModal: React.FC<ModalComponentProps> = (props) => {
  const { closeModal } = props
  
  const handleClick = () => {
    // closeModal has a boolean argument "withOnClose"
    // if it's true, it will trigger the "onClose" prop, if passed
    closeModal(true)
  }
  
  return (
    <div>
      <button onClick={handleClick}>Ok</button>
    </div>
  )
}

How to open and close modals

To control modals you have openModal and closeModal helpers:

import { openModal, closeModal } from '@locmod/modal'

openModal('ExampleModal') // just open a modal that is registered by registerModals() or rendered by standaloneModal

// it returns a unique closer (is the same as closeModal in Modal component)
const closeThisExactModal = openModal('ExampleModal', { 
  // will be triggered by closeThisExactModal(true) or by closeModal(true) inside ExampleModal
  onClose: () => console.log('onclose triggered'),
})

// to close every possible common modal
closeModal('commonModal') // use with careful and only outside the modal itself

When you call openModal, it generates a unique id and an instance of a modal. Such behaviour helps to display multiple modals with the same base component (like CommonModal) and handle them separately.

If a modal has some required props, props argument in openModal is required too. All props will be passed to the modal component.

Registry

Registry is a simple record of modal components keyed by modal name. Each modal will be rendered only when it's opened by manager. If you don't want to load a modal immediately, provide a dynamic component.

const modalRegistry = {
  commonModal: CommonModal, // component
  lazyModal: React.lazy(() => import('compositions/modals/LazyModal/LazyModal')),
  loadableModal: loadable(() => import('compositions/modals/LazyModal/LazyModal')),
  nextjsDynamicModal: dynamic(() => import('compositions/modals/LazyModal/LazyModal')),
}

To register global modals (that could be opened from any place of app) in runtime use registerModals helper:

import { registerModals, ModalsRenderer } from '@locmod/modal'
import InfoModal from './InfoModal/InfoModal'
import ErrorModal from './ErrorModal/ErrorModal'

const modalRegistry = {
  InfoModal,
  ErrorModal,
}

// you can register modals permanently
registerModals(modalRegistry)

const App = () => {
  // or temporary in useEffect
  useEffect(() => {
    return registerModals(modalRegistry)
  }, [])

  return (
    <>
      <Head />
      <Content />
      {/* or via registry prop, which do the same in useEffect */}
      <ModalsRenderer registry={modalRegistry} />
    </>
  )
}

You shouldn't add modals wrapped by standaloneModal to registry, use standaloneModal for non-global modals that used in specific place, e.g. your modal requires some specific Context

Typechecking

To use registered modals in typechecking you need to extend global interface ModalsRegistry:

declare global {
  // dumb but works
  interface ModalsRegistry {
    newModal: { priority?: number }
  }
}

To generate properties from the component, use ExtendModalsRegistry helper. It automatically extracts all properties from the component and removes ModalComponentProps from them:

declare global {
  interface ModalsRegistry extends ExtendModalsRegistry<{ newModal: typeof NewModal }> {}
}