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

@choice-ui/panel

v0.0.8

Published

A panel component for organizing content in collapsible sections with headers and actions

Readme

Panel Component

A comprehensive layout system for creating structured form panels with predefined row layouts, sortable functionality, and collapsible sections. Perfect for property panels, settings interfaces, and complex form layouts.

Overview

The Panel component provides a sophisticated system for organizing form controls and interactive elements in consistent, grid-based layouts. It supports multiple predefined layout patterns, drag-and-drop reordering, collapsible sections, and dynamic content management.

Usage

Basic Panel

import { Panel } from "~/components/panel"

export function BasicExample() {
  return (
    <Panel>
      <Panel.Title title="Basic Panel">
        <IconButton tooltip={{ content: "Settings" }}>
          <Settings />
        </IconButton>
      </Panel.Title>

      <Panel.Row type="single">
        <Panel.Label>Input Label</Panel.Label>
        <Input placeholder="Enter value..." />
      </Panel.Row>
    </Panel>
  )
}

Panel with Labels Toggle

import { Panel } from "~/components/panel"

export function LabelToggleExample() {
  const [showLabels, setShowLabels] = useState(false)

  return (
    <Panel showLabels={showLabels}>
      <Panel.Title title="Properties">
        <Switch
          size="small"
          label="Show labels"
          value={showLabels}
          onChange={setShowLabels}
        />
      </Panel.Title>

      <Panel.Row type="single">
        <Panel.Label>Name</Panel.Label>
        <Input placeholder="Component name" />
      </Panel.Row>
    </Panel>
  )
}

Props

Panel Props

| Prop | Type | Default | Description | | ------------------- | ----------- | ------- | ------------------------------------ | | children | ReactNode | - | Required. Panel content | | showLabels | boolean | true | Whether to show row labels | | collapsible | boolean | false | Whether the panel can be collapsed | | isCollapsed | boolean | - | Controlled collapse state | | onCollapsedChange | function | - | Callback when collapse state changes | | className | string | - | Additional CSS classes |

Sub-components

Panel.Title

Panel header with title and optional action buttons.

<Panel.Title
  title="Panel Title"
  onClick={function}          // Optional click handler
  classNames={{
    container: string,        // Container styles
    titleWrapper: string,     // Title wrapper styles
    title: string,           // Title text styles
    actionWrapper: string    // Actions container styles
  }}
>
  <IconButton tooltip={{ content: "Action" }}>
    <Icon />
  </IconButton>
</Panel.Title>

Panel.Row

Flexible row layout system with predefined grid patterns.

<Panel.Row
  type="single" | "two-columns" | "one-input-one-icon" |
       "one-input-two-icon" | "two-input-one-icon" |
       "two-input-two-icon" | "one-icon-one-input-two-icon" |
       "two-input-one-icon-double-row" | "one-label-one-input" |
       "one-icon-one-input"
>
  {/* Content positioned using grid areas */}
</Panel.Row>

Panel.Label

Label component for form fields.

<Panel.Label className="[grid-area:label]">Field Label</Panel.Label>

Panel.Sortable

Container for sortable list items with drag-and-drop functionality.

<Panel.Sortable
  data={sortableItems}
  selectedId={selectedId}
  onDrop={handleDrop}
  onSelectedIdChange={setSelectedId}
>
  <SortableRowContent />
</Panel.Sortable>

Panel.SortableRow

Individual sortable row with drag handle and grid layout.

<Panel.SortableRow
  type="one-icon-one-input-two-icon"
  onClick={handleClick}
>
  {/* Grid-positioned content */}
</Panel.SortableRow>

Panel.RowManyIcon

Specialized row for multiple icons that show/hide based on editing state.

<Panel.RowManyIcon
  isEditing={boolean}
  icons={[
    {
      id: "icon1",
      element: <IconButton>...</IconButton>,
      alwaysShow?: boolean
    }
  ]}
>
  <EditableContent />
</Panel.RowManyIcon>

Row Layout Types

Single (type="single")

grid-template-areas: "label" "input";
grid-template-columns: 1fr;
grid-template-rows: auto minmax(2rem, auto);
<Panel.Row type="single">
  <Panel.Label>Label</Panel.Label>
  <Select className="[grid-area:input]" />
</Panel.Row>

Two Columns (type="two-columns")

grid-template-areas: "label-1 label-2" "input-1 input-2";
grid-template-columns: 1fr 1fr;
grid-template-rows: auto minmax(2rem, auto);
<Panel.Row type="two-columns">
  <Panel.Label className="[grid-area:label-1]">Left</Panel.Label>
  <Panel.Label className="[grid-area:label-2]">Right</Panel.Label>
  <Input className="[grid-area:input-1]" />
  <Input className="[grid-area:input-2]" />
</Panel.Row>

One Input One Icon (type="one-input-one-icon")

grid-template-areas: "label label" "input icon";
grid-template-columns: 1fr 1.5rem;
grid-template-rows: auto minmax(2rem, auto);
<Panel.Row type="one-input-one-icon">
  <Panel.Label>Field</Panel.Label>
  <Input className="[grid-area:input]" />
  <IconButton className="[grid-area:icon]">
    <Visible />
  </IconButton>
</Panel.Row>

One Input Two Icon (type="one-input-two-icon")

grid-template-areas: "input . icon-1 . icon-2";
grid-template-columns: 1fr 0.5rem 1.5rem 0.25rem 1.5rem;
grid-template-rows: 2rem;

Two Input One Icon (type="two-input-one-icon")

grid-template-areas: "label-1 label-2 label-2" "input-1 input-2 icon";
grid-template-columns: 1fr 1fr 1.5rem;
grid-template-rows: auto 2rem;

Two Input Two Icon (type="two-input-two-icon")

grid-template-areas:
  "label-1 label-1 label-2 label-2 . . ."
  "input-1 . input-2 . icon-1 . icon-2";
grid-template-columns: minmax(76px, 1fr) 0.5rem 1fr 0.5rem 1.5rem 0.25rem 1.5rem;
grid-template-rows: auto minmax(2rem, auto);

One Icon One Input Two Icon (type="one-icon-one-input-two-icon")

grid-template-areas:
  "label label label label label label label"
  "icon-1 . input . icon-2 . icon-3";
grid-template-columns: 1.5rem 0.5rem 1fr 0.5rem 1.5rem 0.25rem 1.5rem;
grid-template-rows: auto minmax(2rem, auto);

Two Input One Icon Double Row (type="two-input-one-icon-double-row")

grid-template-areas:
  "label-1 label-2 ."
  "input-1 input-3 icon-1"
  "input-2 input-3 icon-2";
grid-template-columns: 1fr 1fr 1.5rem;
grid-template-rows: auto 2rem 2rem;

One Label One Input (type="one-label-one-input")

grid-template-areas: "label input";
grid-template-columns: 8fr 20fr;
grid-template-rows: 2rem;

One Icon One Input (type="one-icon-one-input")

grid-template-areas: "label label" "icon input";
grid-template-columns: 1.5rem 1fr;
grid-template-rows: auto minmax(2rem, auto);

Features

Collapsible Panels

import { useState } from "react"

export function CollapsibleExample() {
  const [collapsed, setCollapsed] = useState(false)

  return (
    <Panel
      collapsible
      isCollapsed={collapsed}
      onCollapsedChange={setCollapsed}
    >
      <Panel.Title title="Collapsible Panel">
        <IconButton tooltip={{ content: "Settings" }}>
          <Settings />
        </IconButton>
      </Panel.Title>

      {!collapsed && (
        <Panel.Row type="single">
          <Panel.Label>Content</Panel.Label>
          <Input />
        </Panel.Row>
      )}
    </Panel>
  )
}

Sortable Lists

import { useState } from "react"
import { nanoid } from "nanoid"

export function SortableExample() {
  const [items, setItems] = useState([
    { id: nanoid(), indexKey: "a0", value: "Item 1" },
    { id: nanoid(), indexKey: "a1", value: "Item 2" },
  ])
  const [selectedId, setSelectedId] = useState<string | null>(null)

  const handleDrop = (position, id, newIndex) => {
    // Handle reordering logic
    const newItems = reorderItems(items, id, newIndex, position)
    setItems(newItems)
  }

  const SortableRowContent = () => {
    const item = useSortableRowItem()

    return (
      <Panel.SortableRow
        type="one-input-one-icon"
        onClick={() => setSelectedId(item.id)}
      >
        <Input
          value={item.value}
          className="[grid-area:input]"
        />
        <IconButton className="[grid-area:icon]">
          <Delete />
        </IconButton>
      </Panel.SortableRow>
    )
  }

  return (
    <Panel>
      <Panel.Title title="Sortable Items" />

      <Panel.Sortable
        data={items}
        selectedId={selectedId}
        onDrop={handleDrop}
        onSelectedIdChange={setSelectedId}
      >
        <SortableRowContent />
      </Panel.Sortable>
    </Panel>
  )
}

Multiple Action Icons

export function ManyIconExample() {
  const [isEditing, setIsEditing] = useState(false)

  return (
    <Panel>
      <Panel.Title title="Multiple Icons" />

      <Panel.RowManyIcon
        isEditing={isEditing}
        icons={[
          {
            id: "visible",
            element: (
              <IconButton tooltip={{ content: "Toggle visibility" }}>
                <Visible />
              </IconButton>
            ),
          },
          {
            id: "delete",
            element: (
              <IconButton tooltip={{ content: "Delete" }}>
                <Delete />
              </IconButton>
            ),
          },
          {
            id: "info",
            element: (
              <div className="flex h-6 w-6 items-center justify-center">
                <InfoCircle />
              </div>
            ),
            alwaysShow: true,
          },
        ]}
      >
        <EditableField
          isEditing={isEditing}
          onEditingChange={setIsEditing}
        />
      </Panel.RowManyIcon>
    </Panel>
  )
}

Advanced Examples

Property Panel

export function PropertyPanelExample() {
  const [showLabels, setShowLabels] = useState(true)

  return (
    <Panel showLabels={showLabels}>
      <Panel.Title title="Properties">
        <Switch
          size="small"
          label="Labels"
          value={showLabels}
          onChange={setShowLabels}
        />
        <IconButton tooltip={{ content: "Reset" }}>
          <Reset />
        </IconButton>
      </Panel.Title>

      <Panel.Row type="single">
        <Panel.Label>Name</Panel.Label>
        <Input placeholder="Component name" />
      </Panel.Row>

      <Panel.Row type="two-columns">
        <Panel.Label className="[grid-area:label-1]">Width</Panel.Label>
        <Panel.Label className="[grid-area:label-2]">Height</Panel.Label>
        <NumericInput
          className="[grid-area:input-1]"
          suffix="px"
        />
        <NumericInput
          className="[grid-area:input-2]"
          suffix="px"
        />
      </Panel.Row>

      <Panel.Row type="one-input-two-icon">
        <Panel.Label>Color</Panel.Label>
        <ColorInput className="[grid-area:input]" />
        <IconButton className="[grid-area:icon-1]">
          <Eyedropper />
        </IconButton>
        <IconButton className="[grid-area:icon-2]">
          <Delete />
        </IconButton>
      </Panel.Row>
    </Panel>
  )
}

Settings Panel

export function SettingsPanelExample() {
  return (
    <Panel>
      <Panel.Title title="Settings">
        <IconButton tooltip={{ content: "More options" }}>
          <MoreHorizontal />
        </IconButton>
      </Panel.Title>

      <Panel.Row type="one-label-one-input">
        <div className="text-secondary-foreground [grid-area:label]">Theme</div>
        <Select className="[grid-area:input]">
          <Select.Trigger>
            <span>Dark</span>
          </Select.Trigger>
          <Select.Content>
            <Select.Item value="light">Light</Select.Item>
            <Select.Item value="dark">Dark</Select.Item>
            <Select.Item value="auto">Auto</Select.Item>
          </Select.Content>
        </Select>
      </Panel.Row>

      <Panel.Row type="single">
        <Checkbox>
          <Checkbox.Label>Enable notifications</Checkbox.Label>
        </Checkbox>
      </Panel.Row>
    </Panel>
  )
}

Accessibility

Keyboard Navigation

  • Tab/Shift+Tab: Navigate between interactive elements
  • Enter/Space: Activate buttons and controls
  • Arrow keys: Navigate within grouped controls
  • Escape: Close dropdowns or cancel editing

Focus Management

  • Logical focus order follows visual layout
  • Focus indicators for all interactive elements
  • Proper focus trapping in modals and dropdowns

Screen Reader Support

  • Semantic structure with proper roles and labels
  • Form field associations with labels
  • Status announcements for dynamic changes

Best Practices

Layout Design

  1. Choose appropriate row types for content relationships
  2. Use consistent spacing and alignment
  3. Group related fields logically
  4. Maintain visual hierarchy with proper labeling

Interaction Patterns

  1. Provide clear visual feedback for all actions
  2. Use tooltips for icon-only buttons
  3. Handle loading and error states gracefully
  4. Implement proper keyboard navigation

Performance

  1. Use controlled components efficiently
  2. Minimize unnecessary re-renders
  3. Implement virtualization for long lists
  4. Optimize sortable interactions

Responsive Design

  1. Test layouts at different screen sizes
  2. Consider mobile interaction patterns
  3. Ensure touch targets are appropriately sized
  4. Handle overflow gracefully