Skip to content

Repository files navigation

@lucid-softworks/panel

Compose accessible, unstyled application panels from directly styleable React components.

Dialog manager example

npm install @lucid-softworks/dialog-manager @lucid-softworks/panel
import { useState, type ReactElement } from "react";

import {
  DialogManagerProvider,
  useDialog,
  useManager,
} from "@lucid-softworks/dialog-manager";
import { Panel } from "@lucid-softworks/panel";

interface Confirmation {
  readonly confirmed: true;
}

function ConfirmationPanel(): ReactElement {
  const dialog = useDialog<Confirmation>();

  return (
    <Panel.Root className="panel">
      <Panel.Header className="panel-header">
        <Panel.Title className="panel-title">Delete account?</Panel.Title>
        <Panel.Actions className="panel-actions">
          <button aria-label="Close" onClick={dialog.close} type="button">
            ×
          </button>
        </Panel.Actions>
      </Panel.Header>

      <Panel.Content className="panel-content">
        <p>This action cannot be undone.</p>
      </Panel.Content>

      <Panel.Buttons className="panel-buttons">
        <button onClick={dialog.close} type="button">
          Cancel
        </button>
        <button
          onClick={() => {
            dialog.success({ confirmed: true });
          }}
          type="button"
        >
          Confirm
        </button>
      </Panel.Buttons>
    </Panel.Root>
  );
}

function AccountPage(): ReactElement {
  const manager = useManager();
  const [message, setMessage] = useState("No action taken.");

  function openConfirmation(): void {
    manager.open<Confirmation>({
      id: "delete-account",
      ariaLabel: "Delete account",
      content: <ConfirmationPanel />,
      onClose() {
        setMessage("Deletion cancelled.");
      },
      onSuccess() {
        setMessage("Account deleted.");
      },
    });
  }

  return (
    <main>
      <button onClick={openConfirmation} type="button">
        Delete account
      </button>
      <p>{message}</p>
    </main>
  );
}

export function App(): ReactElement {
  return (
    <DialogManagerProvider>
      <AccountPage />
    </DialogManagerProvider>
  );
}

Composition

Panel.Root, Panel.Header, Panel.Title, Panel.Actions, Panel.Content, and Panel.Buttons each render one semantic HTML element and accept its standard React props. This lets consumers apply plain CSS, CSS modules, Tailwind classes, inline styles, or StyleX props directly.

Panel.Header, Panel.Title, and Panel.Content are required. Panel.Actions and Panel.Buttons are optional. Invalid nesting, missing required parts, and duplicate parts are reported with clear errors.

Panel.Title renders an <h2>. The panel has no dialog-specific behaviour; dialog naming and lifecycle remain the responsibility of the dialog manager.

About

Compose accessible, unstyled application panels from directly styleable React components.

Topics

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages