A comprehensive UI component library for SQLRooms applications, built on top of React and Tailwind CSS. This package provides a collection of reusable, accessible, and customizable components designed to create consistent and beautiful user interfaces.

This library is based on [shadcn/ui](https://ui.shadcn.com/), a collection of beautifully designed, accessible components that can be copied and pasted into your apps.

## Features

- 🎨 **Modern Design**: Clean, modern components following design best practices
- ♿ **Accessibility**: Components built with accessibility in mind
- 🌗 **Theming**: Support for light and dark modes
- 📱 **Responsive**: Mobile-friendly components that adapt to different screen sizes
- 🧩 **Composable**: Components designed to work together seamlessly
- 🔄 **React Hooks**: Useful hooks for common UI patterns

## Installation

```bash
npm install @sqlrooms/ui
# or
yarn add @sqlrooms/ui
```

## Basic Usage

### Using Components

```tsx
import {Button, Card, Input} from '@sqlrooms/ui';

function LoginForm() {
  return (
    <Card className="mx-auto max-w-md p-6">
      <h2 className="mb-4 text-2xl font-bold">Login</h2>
      <form>
        <div className="space-y-4">
          <div>
            <Input type="email" placeholder="Email" required />
          </div>
          <div>
            <Input type="password" placeholder="Password" required />
          </div>
          <Button type="submit" className="w-full">
            Sign In
          </Button>
        </div>
      </form>
    </Card>
  );
}
```

### Using Hooks

```tsx
import {toast, useDisclosure} from '@sqlrooms/ui';

function MyComponent() {
  const {isOpen, onOpen, onClose} = useDisclosure();

  const handleAction = () => {
    // Perform some action
    toast.success('Success!', {
      description: 'Your action was completed successfully.',
    });
    onClose();
  };

  return (
    <div>
      <Button onClick={onOpen}>Open Dialog</Button>
      <Dialog open={isOpen} onOpenChange={onClose}>
        <DialogContent>
          <DialogHeader>
            <DialogTitle>Confirm Action</DialogTitle>
            <DialogDescription>
              Are you sure you want to perform this action?
            </DialogDescription>
          </DialogHeader>
          <DialogFooter>
            <Button variant="outline" onClick={onClose}>
              Cancel
            </Button>
            <Button onClick={handleAction}>Confirm</Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>
    </div>
  );
}
```

## Available Components

- **Layout**: Card, Resizable, SettingsPanelHeader, Tabs
- **Forms**: Button, Checkbox, Combobox, Input, Select, Slider, Switch, Textarea
- **Feedback**: Alert, Progress, Spinner, Toast
- **Navigation**: Accordion, Breadcrumb, Dropdown Menu, TabStrip
- **Overlay**: Dialog, ModifierScrollOverlay, Popover, Tooltip
- **Data Display**: Badge, Table
- **Utility**: Error Boundary, Theme Switch

## Combobox

Use the compound `Combobox` component for searchable select dropdowns built on
the package's Popover and Command primitives.

```tsx
import {Combobox} from '@sqlrooms/ui';

function MySelector() {
  const [value, setValue] = useState('');
  const options = [
    {value: 'option1', label: 'Option 1'},
    {value: 'option2', label: 'Option 2'},
    {value: 'option3', label: 'Option 3'},
  ];
  const selectedLabel =
    options.find((option) => option.value === value)?.label ?? 'Select option';

  return (
    <Combobox value={value} onChange={setValue}>
      <Combobox.Trigger>
        <span>{selectedLabel}</span>
      </Combobox.Trigger>
      <Combobox.Content
        searchable
        searchPlaceholder="Search..."
        emptyMessage="No results found."
      >
        {options.map((option) => (
          <Combobox.Item key={option.value} value={option.value}>
            <span>{option.label}</span>
          </Combobox.Item>
        ))}
      </Combobox.Content>
    </Combobox>
  );
}
```

Available compound components:

- `Combobox` (root) - Manages state and provides context
- `Combobox.Trigger` - Button to open the dropdown
- `Combobox.Content` - Popover content wrapper
- `Combobox.Item` - Individual selectable item

Pass `disabled` to the root `Combobox` to disable opening the dropdown and
selecting items.

For advanced composition, the lower-level `useCombobox` hook is also exported.

## Settings Panel Header

Use `SettingsPanelHeader` for compact settings surfaces that should share the
standard settings icon and optional close affordance.

```tsx
import {Button, SettingsPanelHeader} from '@sqlrooms/ui';
import {CodeIcon} from 'lucide-react';

function SettingsPanel({onClose}: {onClose: () => void}) {
  return (
    <div className="flex h-full flex-col gap-2 p-2">
      <SettingsPanelHeader
        actions={
          <Button type="button" variant="ghost" size="icon">
            <CodeIcon className="h-3.5 w-3.5" />
          </Button>
        }
        onClose={onClose}
      />
      {/* settings controls */}
    </div>
  );
}
```

## Advanced Features

- **Component Composition**: Build complex UIs by composing simple components
- **Form Handling**: Integrated with React Hook Form for easy form management
- **Custom Styling**: Extend components with custom styles using Tailwind CSS
- **Animation**: Smooth transitions and animations for interactive elements
- **`ScrollableRow` forwards its ref and passes through extra props** (e.g.
  `data-*`, `aria-*`, event handlers) to its outermost element, so it can be
  wrapped by a slot component (such as Radix's `Slot`, re-exported from this
  package) without silently losing the ref or those props. Note the two refs
  point at different elements: the forwarded ref is the outer wrapper (the one
  that also takes `className`), while `scrollRef` is the inner scrolling
  container, for reading or setting `scrollLeft`.

## Auto-Resize for Textareas

`useAutoResizeTextarea` is the hook behind `Textarea`'s `autoResize` prop,
exported so it can be applied to a textarea element you did not render
yourself — for example one rendered by a host application's own text-input
component. Give it a ref to the textarea and it grows the element's height to
fit its content, tracks whether the content now exceeds the element's
`max-height`, and re-measures on container resize.

`resizeToFitContent` schedules the measurement on the next animation frame, so
the element's height is not yet updated when the call returns.

```tsx
import {useAutoResizeTextarea} from '@sqlrooms/ui';
import {useRef} from 'react';

function MyTextarea({
  value,
  onChange,
}: {
  value: string;
  onChange: (value: string) => void;
}) {
  const textareaRef = useRef<HTMLTextAreaElement>(null);
  const {hasOverflow, resizeToFitContent} = useAutoResizeTextarea({
    autoResize: true,
    textareaRef,
    value,
  });

  return (
    <textarea
      ref={textareaRef}
      value={value}
      onChange={(event) => onChange(event.currentTarget.value)}
      onInput={() => resizeToFitContent()}
      className={hasOverflow ? 'overflow-y-auto' : 'overflow-y-hidden'}
    />
  );
}
```

`Textarea` itself is unchanged: it still accepts `autoResize` and consumes
this hook internally.

## Native scrolling

Use the `scrollbar-thin` utility for simple native overflow containers that
should use a thin, theme-aware scrollbar:

```tsx
<div className="scrollbar-thin overflow-y-auto">{/* content */}</div>
```

Use `ScrollArea` instead when a surface needs custom horizontal or
bidirectional scrollbar behavior.

## TabStrip

`TabStrip` supports a `fontSize` prop for sizing tab labels, inline rename
inputs, search dropdown content, and built-in subcomponents consistently:

```tsx
<TabStrip
  tabs={tabs}
  openTabs={openTabs}
  selectedTabId={selectedTabId}
  fontSize="12px"
/>
```

Use `renderSearchItemLabel` when the search dropdown should show custom row
content, such as a status spinner next to a tab name.

For more information, visit the SQLRooms documentation.
