import { Meta } from '@storybook/addon-docs/blocks';

<Meta title="FP.React Components/Popover/Readme" />

# Popover

The Popover component uses the native HTML Popover API to display floating content relative to a trigger element. It provides automatic top-layer rendering, light dismiss behavior, and built-in accessibility features.

## Features

- **Native API**: Uses HTML `popover` attribute for automatic layer management
- **Dismiss Modes**: Auto (light dismiss) or manual (explicit close required)
- **Positioning**: CSS anchor positioning with placement hints
- **Accessibility**: Built-in focus management, Escape key handling, and ARIA support
- **Customizable**: CSS custom properties for complete theming control
- **TypeScript**: Full type safety with comprehensive prop types

## Browser Requirements

- Chrome 125+
- Edge 125+
- Safari 17.4+

Requires native Popover API and CSS anchor positioning support.

## Installation

```bash
npm install @fpkit/acss
```

## Basic Usage

```tsx
import { Popover } from '@fpkit/acss';
import '@fpkit/acss/styles';

function App() {
  return (
    <Popover id="my-popover" triggerLabel="Open Menu">
      <h3>Menu</h3>
      <p>Popover content here</p>
    </Popover>
  );
}
```

## Examples

### Default (Auto Mode)

```tsx
<Popover id="default-popover" triggerLabel="Open Popover">
  <h3>Popover Title</h3>
  <p>
    This popover dismisses automatically when you click outside or press
    Escape.
  </p>
</Popover>
```

### Manual Mode

Requires explicit close action and shows a backdrop overlay.

```tsx
<Popover
  id="manual-popover"
  triggerLabel="Open Manual Popover"
  mode="manual"
>
  <h3>Manual Popover</h3>
  <p>
    This popover requires clicking the close button or trigger to dismiss.
    It includes a backdrop overlay.
  </p>
</Popover>
```

### Placement Options

```tsx
{/* Top placement */}
<Popover id="top-popover" triggerLabel="Open Above" placement="top">
  <p>This popover appears above the trigger</p>
</Popover>

{/* Left placement */}
<Popover id="left-popover" triggerLabel="Open Left" placement="left">
  <p>This popover appears to the left</p>
</Popover>

{/* Right placement */}
<Popover id="right-popover" triggerLabel="Open Right" placement="right">
  <p>This popover appears to the right</p>
</Popover>
```

### Custom Trigger

Use any React element as the trigger.

```tsx
<Popover
  id="custom-trigger-popover"
  trigger={
    <button style={{ padding: "0.5rem 1rem", borderRadius: "2rem" }}>
      🎨 Custom
    </button>
  }
>
  <h4>Custom Trigger</h4>
  <p>You can use any React element as trigger</p>
</Popover>
```

### Custom Styling

Theme the popover using CSS custom properties.

```tsx
<Popover
  id="custom-styled-popover"
  triggerLabel="Custom Style"
  styles={{
    '--popover-bg': '#1a1a2e',
    '--popover-border': '0.125rem solid #16213e',
    '--popover-border-radius': '0.75rem',
    '--popover-padding': '1.5rem',
    '--popover-shadow': '0 0.5rem 1rem rgba(0, 0, 0, 0.3)',
    color: '#eee',
  }}
>
  <h3 style={{ color: '#0f3' }}>Dark Theme</h3>
  <p>Customize appearance using CSS custom properties</p>
</Popover>
```

### Controlled State

Manage popover state externally.

```tsx
import { useState } from 'react';

function ControlledExample() {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <>
      <Popover
        id="controlled-popover"
        triggerLabel="Toggle Popover"
        isOpen={isOpen}
        onToggle={setIsOpen}
      >
        <h4>Controlled Popover</h4>
        <p>State is managed externally</p>
        <button onClick={() => setIsOpen(false)}>
          Close via State
        </button>
      </Popover>

      <div>
        <p>Current state: {isOpen ? "Open" : "Closed"}</p>
        <button onClick={() => setIsOpen(!isOpen)}>
          External Toggle
        </button>
      </div>
    </>
  );
}
```

### With Form Content

```tsx
<Popover
  id="form-popover"
  triggerLabel="Show Form"
  mode="manual"
>
  <form
    onSubmit={(e) => {
      e.preventDefault();
      alert('Form submitted!');
    }}
  >
    <h4>Contact Form</h4>
    <input type="text" placeholder="Name" />
    <input type="email" placeholder="Email" />
    <textarea placeholder="Message" rows={3} />
    <button type="submit">Submit</button>
  </form>
</Popover>
```

## API Reference

### Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `id` | `string` | auto-generated | Unique identifier for popover |
| `children` | `ReactNode` | required | Content to display in popover |
| `trigger` | `ReactNode` | - | Custom trigger element |
| `triggerLabel` | `string` | `"Open"` | Aria-label for default trigger |
| `mode` | `"auto" \| "manual"` | `"auto"` | Dismiss behavior |
| `placement` | `"top" \| "bottom" \| "left" \| "right"` | `"bottom"` | Preferred position |
| `isOpen` | `boolean` | - | Controlled open state |
| `onToggle` | `(open: boolean) => void` | - | Toggle callback |
| `showCloseButton` | `boolean` | `true` for manual | Show close button |
| `closeButtonLabel` | `string` | `"Close"` | Aria-label for close button |
| `showArrow` | `boolean` | `true` | Show positioning arrow |
| `className` | `string` | - | Custom CSS class |
| `styles` | `CSSProperties` | - | Inline CSS variables |

### Modes

**Auto Mode** (default)
- Light dismiss: closes on Escape or outside click
- No backdrop overlay
- Close button hidden by default

**Manual Mode**
- Requires explicit close action
- Shows backdrop overlay
- Close button shown by default

## Styling

The Popover component uses CSS custom properties for theming. See the **Styling** page in Storybook for complete styling documentation.

### Quick Theme Example

```tsx
<Popover
  id="themed-popover"
  styles={{
    '--popover-bg': '#1a1a2e',
    '--popover-border': '2px solid #16213e',
    '--popover-border-radius': '12px',
    '--popover-padding': '24px',
  }}
>
  <p>Themed popover</p>
</Popover>
```

## Accessibility

- **Keyboard Navigation**: Escape key closes auto-mode popovers
- **Focus Management**: Automatically managed by browser
- **ARIA Labels**: Proper labeling for triggers and close buttons
- **Screen Readers**: Semantic HTML with proper attributes

## Best Practices

1. **Always provide an ID**: While auto-generated IDs work, explicit IDs are more predictable
2. **Use meaningful trigger labels**: Ensure `triggerLabel` or custom trigger has clear purpose
3. **Choose appropriate mode**: Use auto for menus/tooltips, manual for forms/important content
4. **Test across browsers**: Verify popover support in target browsers
5. **Consider mobile**: Test touch interactions and viewport positioning

## Controlled vs Uncontrolled

### Uncontrolled (Default)

Let the browser manage state automatically:

```tsx
<Popover id="uncontrolled" triggerLabel="Open">
  <p>Content</p>
</Popover>
```

### Controlled

Manage state externally for complex scenarios:

```tsx
const [isOpen, setIsOpen] = useState(false);

<Popover
  id="controlled"
  isOpen={isOpen}
  onToggle={setIsOpen}
  triggerLabel="Open"
>
  <p>Content</p>
</Popover>
```

## Related Components

- **Dialog**: For modal overlays requiring user response
- **Tooltip**: For brief contextual information (consider Popover for richer content)
- **Dropdown**: For selection lists (can be built with Popover)

## Migration from Old Popover

If upgrading from the previous hook-based Popover implementation (`usePopover` or the old `Popover` component from `@fpkit/acss/hooks`), follow this guide:

### Breaking Changes

The legacy Popover has been replaced with a new implementation using the native HTML Popover API. The old implementation is **deprecated** and will be removed in **v3.0.0**.

### Component Migration

**Before (Legacy - Deprecated):**
```tsx
// Old hook-based approach
import { Popover } from '@fpkit/acss/hooks'; // ❌ Deprecated
// or
import { usePopover } from '@fpkit/acss/hooks'; // ❌ Deprecated

<Popover popoverTrigger={<button>Hover me</button>}>
  <p>Hover-based popover content</p>
</Popover>
```

**After (New - Recommended):**
```tsx
// New native Popover API approach
import { Popover } from '@fpkit/acss'; // ✅ Recommended

<Popover
  id="my-popover"
  trigger={<button>Click me</button>}
>
  <p>Click-based popover content</p>
</Popover>
```

### Hook Migration

If you were using the `usePopover` hook directly for custom implementations:

**Before (Legacy - Deprecated):**
```tsx
import { usePopover } from '@fpkit/acss/hooks'; // ❌ Deprecated

function CustomPopover() {
  const hoverRef = useRef(null);
  const popoverRef = useRef(null);
  const { isVisible, popoverPosition, handlePointerEvent, handlePointerLeave } =
    usePopover(hoverRef, popoverRef, 10);

  return (
    <div>
      <div
        ref={hoverRef}
        onPointerEnter={handlePointerEvent}
        onPointerLeave={handlePointerLeave}
      >
        Hover trigger
      </div>
      {isVisible && (
        <div
          ref={popoverRef}
          style={{
            position: 'absolute',
            top: popoverPosition.top,
            left: popoverPosition.left,
          }}
        >
          Popover content
        </div>
      )}
    </div>
  );
}
```

**After (New - Recommended):**
```tsx
import { Popover } from '@fpkit/acss'; // ✅ Recommended

function CustomPopover() {
  return (
    <Popover
      id="custom-popover"
      trigger={<button>Click trigger</button>}
      placement="bottom"
    >
      <p>Popover content</p>
    </Popover>
  );
}
```

### Key Behavioral Changes

| Feature | Old (Legacy) | New (Native API) |
|---------|-------------|-----------------|
| **Trigger Method** | Hover/pointer events | Click (default native behavior) |
| **Positioning** | Manual calculation | Native CSS anchor positioning |
| **Layer Management** | z-index stacking | Automatic top-layer rendering |
| **Dismiss Behavior** | Manual pointer leave | Native light dismiss (Escape, outside click) |
| **Focus Management** | Manual implementation | Automatic browser handling |
| **Accessibility** | Limited ARIA support | Full native accessibility |
| **Browser Support** | All modern browsers | Chrome 125+, Safari 17.4+, Edge 125+ |

### Prop Mapping

| Old Prop | New Prop | Notes |
|----------|----------|-------|
| `popoverTrigger` | `trigger` | Custom trigger element |
| `content` | `children` | Popover content |
| N/A | `id` | Required for native API (auto-generated if omitted) |
| N/A | `mode` | `"auto"` or `"manual"` dismiss behavior |
| N/A | `placement` | `"top"`, `"bottom"`, `"left"`, `"right"` |
| N/A | `isOpen` | Controlled state support |
| N/A | `onToggle` | Callback when popover opens/closes |
| N/A | `showArrow` | Show positioning arrow indicator |
| N/A | `styles` | CSS custom properties for theming |

### Benefits of Migration

✅ **Better Accessibility**: Native focus management and keyboard navigation
✅ **Simpler API**: No manual positioning calculations required
✅ **Better Performance**: Browser-native layer management
✅ **Future-Proof**: Uses web platform standards
✅ **Rich Features**: Built-in animations, backdrops, and light dismiss
✅ **Type Safety**: Full TypeScript support with comprehensive prop types

### Migration Checklist

- [ ] Replace `import { Popover } from '@fpkit/acss/hooks'` with `import { Popover } from '@fpkit/acss'`
- [ ] Replace `import { usePopover }` with the new Popover component
- [ ] Change `popoverTrigger` prop to `trigger`
- [ ] Move popover content to `children` prop
- [ ] Add unique `id` prop (recommended but optional)
- [ ] Choose `mode="auto"` (default) or `mode="manual"`
- [ ] Test browser compatibility (Chrome 125+, Safari 17.4+, Edge 125+)
- [ ] Update any custom styling to use CSS custom properties
- [ ] Test keyboard navigation (Escape key, Tab order)
- [ ] Verify accessibility with screen readers

### Fallback for Older Browsers

The native Popover API is not supported in older browsers. Consider:

1. **Feature Detection**: Check for popover support before using
2. **Polyfill**: Use a popover polyfill for older browser support
3. **Progressive Enhancement**: Provide fallback UI for unsupported browsers

```tsx
// Feature detection example
if (!HTMLElement.prototype.hasOwnProperty('popover')) {
  console.warn('Popover API not supported - consider polyfill');
}
```

### Need Help?

- Review the **Examples** section above for common use cases
- Check the **API Reference** for complete prop documentation
- See the **Styling** page for theming and customization options
- Open an issue on GitHub if you encounter migration problems

## Troubleshooting

### Popover not appearing

Check browser support and console for errors about popover API.

### Positioning issues

CSS anchor positioning requires Chrome 125+. Older browsers use fallback positioning.

### Animation not working

Ensure `@starting-style` is supported or provide fallback transitions.

### Close button not showing

For auto mode, close button is hidden by default. Set `showCloseButton={true}` to show.
