# Application patterns

Shells, page headers, forms, search, transfer lists and command palettes.

## Quick start

After the [application setup](/docs/installation/#component-consumption), copy this component into your application. It imports only public packages; the complete live demo later on this page also uses documentation layout helpers.

```tsx
'use client';
import { useState } from 'react';
import { NavigationDrawer } from '@fayegram-ai/faye-ui/patterns/navigation-drawer';
import { PageHeader } from '@fayegram-ai/faye-ui/patterns/content';

export function ExploreLayout() {
  const [open, setOpen] = useState(true);
  return <NavigationDrawer open={open} onOpenChange={setOpen} title="Explore"
    navigation={<nav aria-label="Places"><a href="/places">Places</a></nav>}>
    <main><PageHeader title="Your next small adventure" description="Choose a place to begin." /></main>
  </NavigationDrawer>;
}
```

API reference: [Navigation Drawer](/docs/api-patterns-navigation-drawer/), [Content](/docs/api-patterns-content/).

## Usage contract
Patterns use public primitives and keep business logic outside the library. AppShell supports a configurable main element for embedded previews so documents do not accidentally contain nested main landmarks. PageHeader exposes heading level independently from visual size.

`CommandPalette` filters supplied labels and keywords, preserves disabled commands, and delegates keyboard selection and focus to Combobox/Dialog. Its search clears on selection, dismissal, and a parent changing `open` to `false`, so reopening starts with the full command list. Global shortcuts and remote request ordering remain application-owned.

## Customization
Use semantic tokens for visual changes, public component parts for structural customization, and explicit props for instance behavior. The surrounding site lets you compare light and dark, different brands and density.

## Source and verification
The examples below import actual public package entries. Full source is downloadable. Source coverage does not establish keyboard, hydration or browser compatibility; those require the application test suite.

## Shell height and scroll ownership

`AppShell` is a static, server-safe layout. Its defaults remain `height="viewport"` and `scroll="document"`. Use `height="content"` with `mainAs="section"` for embedded previews. Use `height="parent"` inside an explicitly bounded parent. `scroll="workspace"` makes the main area and sidebar scroll independently for viewport/parent modes; content mode remains intrinsic. Header/footer remain outside that main scroller.

Responsive navigation, collapse state, routing and domain records belong to the application. The workshop demonstrates a desktop rail, a narrow-screen Drawer with dismissal/focus return, and hash-based list/detail/settings history. Its compact default is an application preference; the provider default remains comfortable.

The shell example below creates and filters actual local drafts. The full workshop keeps saved data in versioned sessionStorage for the current tab, with an in-memory fallback and explicit reset. Drafts remain separate until save; cancel restores the saved baseline. Deletion offers undo until another deletion, dismissal or reload. The local service toggle demonstrates failure, cancellation and retry without contacting a backend.

## Transfer-list bulk selection

`TransferList.showSelectAll` adds a select-all/none checkbox and selected-count feedback per side. It ignores disabled items, reports a mixed state for partial selection, and keeps the item value controlled. Moving items restores focus to the destination group, including an empty group. `getSelectAllLabel` and the existing labels/announcements support localization.

## Compact composition defaults

PageHeader defaults to the `lg` title size; use `titleSize` to select another visual size independently of `headingLevel`. The surrounding Stack owns the gap to filters and content.

CommandPalette focuses its search input when opened. Its header includes an icon dismissal action with the accessible `closeLabel`; `closeIcon` replaces the artwork. Results scroll within the bounded palette and no-results text occupies space only when present.

## Executable example source

```tsx
'use client';
import { useState } from 'react';
import { Folder, Activity, Plus, X } from '@lucide/icons';
import { Icon } from '@fayegram-ai/faye-ui/core/icon';
import { AppShell } from '@fayegram-ai/faye-ui/patterns/shell';
import { PageHeader, EmptyState, FormSection, FilterBar } from '@fayegram-ai/faye-ui/patterns/content';
import { AppBar, BottomNavigation } from '@fayegram-ai/faye-ui/patterns/application-navigation';
import { SearchField } from '@fayegram-ai/faye-ui/patterns/search-field';
import { CommandPalette } from '@fayegram-ai/faye-ui/patterns/command-palette';
import { TransferList } from '@fayegram-ai/faye-ui/patterns/transfer-list';
import { FloatingAction, FloatingActions } from '@fayegram-ai/faye-ui/patterns/floating-actions';
import { Button } from '@fayegram-ai/faye-ui/controls/button';
import { TextField } from '@fayegram-ai/faye-ui/controls/text-field';
import { Stack } from '@fayegram-ai/faye-ui/layout/layout';
import { Section, Result } from '../example';
export default function Patterns() {
    const [query, setQuery] = useState(''), [command, setCommand] = useState(false), [result, setResult] = useState('No command selected'), [chosen, setChosen] = useState<string[]>(['design']), [dock, setDock] = useState(false), [action, setAction] = useState(false);
    const [projects, setProjects] = useState<string[]>([]);
    const create = () => { setProjects(all => [...all, `Draft project ${all.length + 1}`]); setQuery(''); setResult('Draft created in the workspace above'); };
    const matches = projects.filter(project => project.toLowerCase().includes(query.toLowerCase()));
    return <><Section title="Application shell"><AppShell height="content" sidebar={<nav aria-label="Preview navigation"><a href="#workspace-preview">Workspace</a></nav>} header={<AppBar>Example workspace</AppBar>} mainAs="section" mainId="workspace-preview" mainLabel="Workspace example"><Stack gap="4"><PageHeader headingLevel={3} title="Projects" description="Application patterns compose the same primitives." actions={<Button onClick={() => setCommand(true)}>Commands</Button>}/><FilterBar><SearchField label="Filter example projects" placeholder="Find a project" value={query} onChange={e => setQuery(e.target.value)}/><Button onClick={() => setQuery('')}>Clear</Button></FilterBar>{matches.length ? <><ul aria-label="Example projects">{matches.map(project => <li key={project}>{project}</li>)}</ul><Button onClick={create}>Create draft</Button></> : <EmptyState title={projects.length ? 'No matching projects' : 'No projects in this example'} description={projects.length ? 'Clear the filter or create another draft.' : 'Create a draft to begin.'} action={<Button onClick={create}>Create draft</Button>}/>}</Stack></AppShell><BottomNavigation current="projects" items={[{ key: 'projects', label: 'Projects', icon: <Icon icon={Folder}/>, href: '#workspace-preview' }, { key: 'activity', label: 'Activity', icon: <Icon icon={Activity}/>, href: '/docs/display/' }]}/></Section><Section title="Form section and transfer pattern"><FormSection title="Project settings" description="Preferences remain application-owned."><TextField label="Team" defaultValue="Design systems"/></FormSection><TransferList showSelectAll items={[{ id: 'design', label: 'Design' }, { id: 'engineering', label: 'Engineering' }, { id: 'research', label: 'Research' }]} value={chosen} onValueChange={setChosen}/><Result>Selected: {chosen.join(', ')}</Result></Section><Section title="Command palette and optional action docks"><Button onClick={() => setCommand(true)}>Open commands</Button><CommandPalette open={command} onOpenChange={setCommand} commands={[{ id: 'create', label: 'Create project', keywords: ['new'] }, { id: 'archive', label: 'Archive latest project', disabled: projects.length === 0 }, { id: 'publish', label: 'Publish', disabled: true }]} onSelect={item => { if (item.id === 'create') create(); else if (item.id === 'archive') { setProjects(all => all.slice(0, -1)); setResult('Latest project archived'); } }}/><Button onClick={() => { setDock(v => !v); setAction(false); }}>Toggle action menu dock</Button><Button onClick={() => { setAction(v => !v); setDock(false); }}>Toggle single floating action</Button>{dock && <FloatingActions label="Quick actions" items={[{ id: 'create', label: <><Icon icon={Plus}/>Create a draft</>, onSelect: create }, { id: 'close', label: <><Icon icon={X}/>Hide dock</>, onSelect: () => setDock(false) }]}/>} {action && <FloatingAction onClick={() => { create(); setAction(false); }}><Icon icon={Plus}/>Create draft</FloatingAction>}<Result>{result}</Result></Section></>;
}
```
