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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@linzjs/windows

v9.5.6

Published

[![semantic-release: react](https://img.shields.io/badge/semantic--release-react-e10079?logo=semantic-release)](https://github.com/semantic-release/semantic-release)

Readme

@linzjs/windows

semantic-release: react

Note: Make sure to install oxc plugin in Intellij/VSCode for development.

Reusable promise based windowing component for LINZ / Toitū te whenua.

React state based modals/windows are painful because they require:

  • shared states for open/closed.
  • callbacks/states for return values.
  • inline modal/window includes, which prevent you from closing the invoking component before the modal/window has completed.

This module gives you promise based modals/windows which don't require all the state based boilerplate / inline-components.

So you can simply do this in your react-app:

const result = await showModal(TestModal, { props... })

<button onClick={() => openPanel({ componentFn: () => <Panel.../>, onClose=onClose)}>
    Open panel
</button>

Features

  • Async HTML dialog based Modals.
  • Draggable and resizeable, pop-in/out Panels/Windows.

Install

npm install @linzjs/windows

or with Yarn

yarn add @linzjs/windows

Debug log

To enable debug logs set:

localStorage.setItem("@linzjs/windows.debugEnabled", "true");

Demo

npm run storybook

Components

Modal (LuiModalAsync)

Promise-based async modals using the HTML <dialog> element. Show a modal, await the result, and continue.

Generic modal example

Setup — wrap your app with the context provider once at the root:

import { LuiModalAsyncContextProvider } from '@linzjs/windows';

export const App = () => (
  <LuiModalAsyncContextProvider>
    <div>...the rest of your app...</div>
  </LuiModalAsyncContextProvider>
);

Define a modal component — extend your props with LuiModalAsyncCallback<RESULT_TYPE> to declare the return type and receive resolve:

import React, { ReactElement } from 'react';
import {
  LuiModalAsync,
  LuiModalAsyncButtonContinue,
  LuiModalAsyncButtonDismiss,
  LuiModalAsyncButtonGroup,
  LuiModalAsyncCallback,
  LuiModalAsyncContent,
  LuiModalAsyncHeader,
  LuiModalAsyncMain,
} from '@linzjs/windows';

// The modal returns a number (or undefined if dismissed/escaped)
interface TestModalProps extends LuiModalAsyncCallback<number> {
  text: string;
}

const TestModal = ({ text, resolve }: TestModalProps): ReactElement => {
  const doSomething = () => {
    resolve(10); // close modal and return 10
  };

  return (
    <LuiModalAsync closeOnOverlayClick={false}>
      <LuiModalAsyncMain>
        <LuiModalAsyncHeader title={'Generic modal'} onHelpClick={() => alert('help link')} />
        <LuiModalAsyncContent>{text}</LuiModalAsyncContent>
      </LuiModalAsyncMain>
      <LuiModalAsyncButtonGroup>
        <LuiModalAsyncButtonDismiss autofocus={true}>Dismiss</LuiModalAsyncButtonDismiss>
        <LuiModalAsyncButtonContinue level={'tertiary'} onClick={doSomething}>
          Continue onClick
        </LuiModalAsyncButtonContinue>
        <LuiModalAsyncButtonContinue value={10}>Continue resolve value</LuiModalAsyncButtonContinue>
      </LuiModalAsyncButtonGroup>
    </LuiModalAsync>
  );
};

Invoke the modal — use useShowAsyncModal, await the result:

import { useShowAsyncModal } from '@linzjs/windows';

export const TestModalUsage = () => {
  // modalOwnerRef is only required if you have popout windows
  const { showModal, modalOwnerRef } = useShowAsyncModal();

  const showModalHandler = async () => {
    const result = await showModal(
      TestModal,
      { text: "I'm generic modal content" },
      { showOnAllWindows: true },
    );

    if (!result) return alert('Modal closed');
    alert(`Modal result is: ${String(result)}`);
  };

  // Add modalOwnerRef so that modals work in popout windows
  return (
    <div ref={modalOwnerRef}>
      <button onClick={() => void showModalHandler()}>Click to show the modal</button>
    </div>
  );
};

Prefab Modal

Pre-built modals for common use cases: outline, info, warning, error, success, progress, and blocked.

| Outline | Info | Warning | Error | |---------|------|---------|-------| | Outline modal | Info modal | Warning modal | Error modal |

| Success | Progress | Blocked | Custom buttons | |---------|----------|---------|----------------| | Success modal | Progress modal | Blocked modal | Custom buttons modal |

Setup — same LuiModalAsyncContextProvider as above.

Invoke — use useLuiModalPrefab:

import { useLuiModalPrefab, LuiModalDontShowSessionRemove } from '@linzjs/windows';

export const PrefabModalUsage = () => {
  const { modalOwnerRef, showPrefabModal } = useLuiModalPrefab();

  return (
    <div ref={modalOwnerRef} style={{ display: 'flex', gap: 10 }}>
      {/* Info */}
      <button
        onClick={() =>
          void showPrefabModal({
            level: 'info',
            title: 'You are a fantastic person',
            children: 'Keep it up!',
          })
        }
      >
        Info
      </button>

      {/* Warning with custom buttons */}
      <button
        onClick={() =>
          void showPrefabModal<'delete'>({
            level: 'warning',
            title: 'You are about to make changes',
            children: 'Are you sure that you want to make these changes?',
            helpLink: 'https://www.example.com/help',
            buttons: [
              { title: 'Cancel', icon: 'ic_navigate_before', value: undefined },
              { title: 'Delete the world!', icon: 'ic_delete_solid', value: 'delete', level: 'error' },
            ],
          }).then((result) => {
            alert('Warning result: ' + result);
          })
        }
      >
        Warning
      </button>

      {/* Warning with "don't show again" */}
      <button
        onClick={() =>
          void showPrefabModal({
            level: 'warning',
            title: 'Warning',
            children: 'This message can be suppressed.',
            dontShowAgainSessionKey: 'userId_surveyId_modalId',
            buttons: [{ title: 'Dismiss', default: true }],
          })
        }
      >
        Warning + Don't show again
      </button>

      {/* Error */}
      <button
        onClick={() =>
          void showPrefabModal({
            level: 'error',
            title: 'Something is not right',
            children: 'Maybe stop doing that',
            onHelpClick: () => {},
          })
        }
      >
        Error
      </button>

      {/* Success */}
      <button
        onClick={() =>
          void showPrefabModal({
            level: 'success',
            title: 'You are a success',
            children: 'Keep succeeding!',
            onHelpClick: () => {},
          })
        }
      >
        Success
      </button>

      {/* Progress — polledCloseCondition closes the modal automatically */}
      <button
        onClick={() => {
          let done = false;
          setTimeout(() => (done = true), 5000);
          void showPrefabModal({
            level: 'progress',
            title: 'Signing in progress',
            children: 'This modal will close in 5 seconds unless cancelled.',
            buttons: [{ title: 'Cancel', value: true }],
            polledCloseCondition: () => done,
          }).then((result) => {
            alert(result === true ? 'Cancelled' : 'Timed-out');
          });
        }}
      >
        Progress
      </button>

      {/* Blocked */}
      <button
        onClick={() =>
          void showPrefabModal({
            level: 'blocked',
            title: 'Plan Generation blocked',
            children: "This CSD is being used by 'Joe Bloggs'. Please close Plan Generation.",
            buttons: [{ title: 'Return to Survey Capture' }],
          })
        }
      >
        Blocked
      </button>
    </div>
  );
};

To clear a "don't show again" session key programmatically:

import { LuiModalDontShowSessionRemove } from '@linzjs/windows';

LuiModalDontShowSessionRemove('userId_surveyId_modalId');

Upload Modal

A prefab modal for file upload interactions.

Upload modal

Setup — same LuiModalAsyncContextProvider as above.

Invoke — use useLuiModalUpload:

import { useLuiModalUpload } from '@linzjs/windows';

export const ModalUploadUsage = () => {
  const { modalOwnerRef, showUploadModal } = useLuiModalUpload();

  return (
    <div ref={modalOwnerRef}>
      <button
        onClick={() =>
          void showUploadModal({
            title: 'Add georeferenced image',
            content: 'Once your image has been uploaded, place markers to align the image to the spatial view.',
            width: 480,
            fileDescription: 'image',
            fileFormatText: 'Format: jpg, jpeg, png',
            acceptedExtensions: ['jpg', 'jpeg', 'png'],
            customFileErrorMessage: 'Incorrect file format!',
          }).then((file) => {
            console.log(file);
          })
        }
      >
        Upload Image...
      </button>
    </div>
  );
};

Panel

Draggable, resizeable, pop-in/pop-out panel windows. Panels are opened imperatively via OpenPanelButton or openPanel.

Panel

Setup — wrap your app with the context provider once at the root:

import { PanelsContextProvider } from '@linzjs/windows';

export const App = () => (
  <PanelsContextProvider baseZIndex={500}>
    <div>...the rest of your app...</div>
  </PanelsContextProvider>
);

Define a panel component:

import { Panel, PanelContent, PanelHeader } from '@linzjs/windows';

export const ShowPanelComponent = ({ data }: { data: number }) => (
  <Panel title={`Panel demo ${data}`} size={{ width: 640, height: 'auto' }}>
    <PanelHeader
      helpUrl={'#help'}
      icon={'ic_add_adopt'}
      onHelpClick={() => alert('Help!')}
    />
    <PanelContent>
      {/* panel body content */}
    </PanelContent>
  </Panel>
);

// Panel as modal (disables popout, uses fixed height)
export const ShowPanelModalComponent = ({ data }: { data: number }) => (
  <Panel title={`Panel demo ${data}`} size={{ width: 640, height: 400 }} modal={true}>
    <PanelHeader icon={'ic_add_adopt'} disablePopout={true} />
    <PanelContent>
      {/* panel body content */}
    </PanelContent>
  </Panel>
);

Open panels — use OpenPanelButton or the openPanel function:

import { OpenPanelButton } from '@linzjs/windows';

export const TestShowPanel = () => (
  <div style={{ display: 'flex', gap: 8 }}>
    {/* uniqueId prevents duplicate panels */}
    <OpenPanelButton
      buttonText={'Panel 1'}
      testId={'panel1'}
      componentFn={() => <ShowPanelComponent data={1} />}
      uniqueId="panel1"
    />
    <OpenPanelButton
      buttonText={'Panel 2'}
      testId={'panel2'}
      componentFn={() => <ShowPanelComponent data={2} />}
      uniqueId="panel2"
    />
    <OpenPanelButton
      buttonText={'Panel as a Modal'}
      componentFn={() => <ShowPanelModalComponent data={3} />}
    />
    {/* Panel with dynamic bounds — recalculated on resize */}
    <OpenPanelButton
      buttonText={'Dynamic bounds panel'}
      componentFn={() => (
        <ShowPanelModalComponent
          data={3}
          resizeable={false}
          dynamicBounds={() => ({
            x: window.innerWidth / 3,
            y: window.innerHeight / 4,
            height: 'auto',
            width: window.innerWidth * 0.4,
          })}
        />
      )}
    />
  </div>
);

Panel props:

| Prop | Type | Description | |------|------|-------------| | title | string | Panel title bar text | | size | { width: number, height: number \| 'auto' } | Initial panel size | | modal | boolean | Disable dragging and popout, center on screen | | resizeable | boolean | Allow user resizing (default true) | | maxHeight | number | Maximum height in px | | maxWidth | number | Maximum width in px | | minHeight | number | Minimum height in px | | minWidth | number | Minimum width in px | | dynamicBounds | () => { x, y, width, height } | Callback to recalculate position/size dynamically |

Saving panel state — pass panelStateOptions to PanelsContextProvider or OpenPanelButton:

<OpenPanelButton
  buttonText={'Panel'}
  componentFn={() => <ShowPanelComponent data={1} />}
  panelStateOptions={{
    saveStateIn: 'localStorage',
    saveStateKey: 'userId',
  }}
/>

Panel with Docking

Panels can dock into a designated PanelDock area. Add dockTo to PanelHeader and place a PanelDock in your layout.

import { OpenPanelButton, Panel, PanelContent, PanelDock, PanelHeader } from '@linzjs/windows';
import { useState } from 'react';

// Panel that can dock to the left side
export const DockablePanel = ({ data }: { data: number }) => (
  <Panel title={`Panel demo ${data}`} size={{ width: 640, height: 400 }}>
    <PanelHeader dockTo={'leftSide'} />
    <PanelContent>
      {/* panel body content */}
    </PanelContent>
  </Panel>
);

export const TestShowPanel = () => {
  const [visible, setVisible] = useState(true);
  return (
    <>
      <div style={{ display: 'flex', gap: 8 }}>
        <OpenPanelButton buttonText={'panel'} componentFn={() => <DockablePanel data={1} />} />
        <button onClick={() => setVisible(!visible)}>Toggle dock visible</button>
      </div>
      {/* The panel docks into this area when the user clicks the dock button */}
      {visible && <PanelDock id={'leftSide'}>The Panel will dock in here</PanelDock>}
    </>
  );
};

Tabbed Panel

Panels support tabs via the @linzjs/lui LuiTabs components. Wrap the Panel in LuiTabs and use LuiTabsGroup/LuiTabsPanelSwitch in the header.

Tabbed panel example screenshot

import { LuiTabs, LuiTabsGroup, LuiTabsPanel, LuiTabsPanelSwitch } from '@linzjs/lui';
import { OpenPanelButton, Panel, PanelContent, PanelHeader } from '@linzjs/windows';

export const TabbedPanelComponent = ({ data }: { data: number }) => (
  <LuiTabs defaultPanel="africa">
    <Panel title={`Panel demo ${data}`} size={{ width: 640, height: 400 }}>
      <PanelHeader
        icon={'ic_send'}
        onHelpClick={() => alert('Help!!!')}
        extraLeft={
          <LuiTabsGroup ariaLabel="Animals">
            <LuiTabsPanelSwitch targetPanel="africa">Africa</LuiTabsPanelSwitch>
            <LuiTabsPanelSwitch targetPanel="asia">Asia</LuiTabsPanelSwitch>
          </LuiTabsGroup>
        }
      />
      <PanelContent>
        <LuiTabsPanel panel="africa">
          <h2>African Countries</h2>
        </LuiTabsPanel>
        <LuiTabsPanel panel="asia">
          <h2>Asian Countries</h2>
        </LuiTabsPanel>
      </PanelContent>
    </Panel>
  </LuiTabs>
);

export const TestShowTabbedPanel = () => (
  <>
    <OpenPanelButton buttonText={'TestPanel 1'} componentFn={() => <TabbedPanelComponent data={1} />} />
    <OpenPanelButton buttonText={'TestPanel 2'} componentFn={() => <TabbedPanelComponent data={2} />} />
  </>
);

Panel with Global Modal

When a modal is shown from inside a panel that has been popped out into a separate window, use showOnAllWindows: true to ensure the modal appears in the correct window.

import { useLuiModalPrefab, OpenPanelButton, Panel, PanelContent, PanelHeader } from '@linzjs/windows';

// showOnAllWindows ensures the modal appears even in popped-out windows
const PanelContents = () => {
  const { showPrefabModal } = useLuiModalPrefab();

  return (
    <div>
      <button
        onClick={() =>
          void showPrefabModal({
            showOnAllWindows: true,
            level: 'info',
            title: 'You are a fantastic person',
            children: 'Keep it up!',
          })
        }
      >
        Show modal
      </button>
    </div>
  );
};

export const TestShowPanelWithGlobalModal = () => (
  <>
    <OpenPanelButton buttonText={'TestPanel 1'} componentFn={() => (
      <Panel title={'Panel demo 1'} size={{ width: 640, height: 400 }}>
        <PanelHeader icon={'ic_send'} onHelpClick={() => alert('Help!!!')} />
        <PanelContent><PanelContents /></PanelContent>
      </Panel>
    )} />
  </>
);

Ribbon

A toolbar ribbon with buttons, sliders, menus, and separators. Supports horizontal and vertical orientations.

ribbon-slider.pngimg.png ribbon-panel.png ribbon-menu.png Setup — requires both LuiModalAsyncContextProvider and PanelsContextProvider:

import {
    RibbonButton,
    RibbonButtonLink,
    RibbonButtonOpenPanel,
    RibbonButtonSlider,
    RibbonContainer,
    RibbonSeparator,
    RibbonMenu,
    RibbonMenuOption,
    RibbonMenuSeparator,
    Panel,
    PanelContent,
    PanelHeader,
} from '@linzjs/windows';
import { useState } from 'react';

export const TestRibbonPanel = () => {
    const [selectedItem, setSelectedItem] = useState('ic_add_rectangle');
    const [loading, setLoading] = useState(false);
    const [processing, setProcessing] = useState(false);

    return (
        <>
            <button onClick={() => setLoading(!loading)}>Toggle loading</button>
            <button onClick={() => setProcessing(!processing)}>Toggle processing</button>

            {/* Horizontal ribbon */}
            <RibbonContainer style={{ position: 'absolute', left: 220, top: 120 }}>

                {/* Button that opens a panel — disabled state */}
                <RibbonButtonOpenPanel
                    disabled
                    title={'Marks'}
                    icon={'ic_marks'}
                    componentFn={() => <Panel title="Marks" size={{ width: 640, height: 400 }}><PanelHeader /><PanelContent /></Panel>}
                    loading={loading}
                />

                <RibbonSeparator />

                {/* Button that opens a panel — with processing indicator */}
                <RibbonButtonOpenPanel
                    title={'Vectors'}
                    icon={'ic_line_irregular'}
                    componentFn={() => <Panel title="Vectors" size={{ width: 640, height: 400 }}><PanelHeader /><PanelContent /></Panel>}
                    loading={loading}
                    processing={processing}
                    processingMessage={'Validating...'}
                />

                {/* Slider button — opens a sub-panel of options below */}
                <RibbonButtonSlider title={selectedItem} icon={selectedItem} alignment={'down'} loading={loading}>
                    <RibbonContainer orientation={'vertical'}>
                        <RibbonButton
                            title={'Rectangle'}
                            icon={'ic_add_rectangle'}
                            selected={selectedItem === 'ic_add_rectangle'}
                            onClick={() => setSelectedItem('ic_add_rectangle')}
                        />
                        <RibbonButton
                            title={'Other'}
                            icon={'ic_polygon_selection'}
                            selected={selectedItem === 'ic_polygon_selection'}
                            onClick={() => setSelectedItem('ic_polygon_selection')}
                        />
                    </RibbonContainer>
                </RibbonButtonSlider>

                {/* Slider button — dropdown menu with autoClose */}
                <RibbonButtonSlider title={'Other'} icon={'ic_more_vert'} alignment={'down'} autoClose={true} loading={loading}>
                    <RibbonMenu>
                        <RibbonMenuOption icon={'ic_define_nonprimary_diagram_circle'}>Circle</RibbonMenuOption>
                        <RibbonMenuOption icon={'ic_define_nonprimary_diagram_rectangle'} disabled={true}>Rectangle</RibbonMenuOption>
                        <RibbonMenuSeparator />
                        <RibbonMenuOption>Item 3</RibbonMenuOption>
                        <RibbonMenuSeparator />
                        <RibbonMenuOption icon={'ic_clear'}>Cancel</RibbonMenuOption>
                    </RibbonMenu>
                </RibbonButtonSlider>
            </RibbonContainer>

            {/* Vertical ribbon */}
            <RibbonContainer orientation={'vertical'} style={{ position: 'absolute', left: 220, top: 180 }}>
                <RibbonButtonOpenPanel
                    title={'Vectors'}
                    icon={'ic_timeline'}
                    componentFn={() => <Panel title="Vectors" size={{ width: 640, height: 400 }}><PanelHeader /><PanelContent /></Panel>}
                    loading={loading}
                />
                {/* Slider opens to the right */}
                <RibbonButtonSlider title={selectedItem} icon={selectedItem} alignment={'right'} loading={loading}>
                    <RibbonContainer>
                        <RibbonButton
                            title={'Rectangle'}
                            icon={'ic_add_rectangle'}
                            selected={selectedItem === 'ic_add_rectangle'}
                            onClick={() => setSelectedItem('ic_add_rectangle')}
                        />
                    </RibbonContainer>
                </RibbonButtonSlider>
                {/* External link button */}
                <RibbonButtonLink href={'https://example.com/'} icon={'ic_link'} loading={loading} />
            </RibbonContainer>
        </>
    );
};

RibbonButtonSlider alignment options: 'down' | 'up' | 'left' | 'right' | 'left-up' | 'right-up' | 'right-center'