import { Canvas, Meta, Source } from '@storybook/addon-docs/blocks'
import { LifecycleTag } from '../../docs/components'
import {
  TableRoot,
  TableHeader,
  TableRow,
  TableHead,
  TableBody,
  TableCell,
} from '../../components/Table'
import * as MediaQueriesStories from './MediaQueries.stories'

<Meta of={MediaQueriesStories} />

# Media Queries Hooks

<LifecycleTag variant="Stable" />

A collection of optimized hooks for handling media queries and viewport breakpoints in React components.

- [useViewport](#useviewport) - Detect the current active design breakpoint (XL, L, M, S, XS).
- [useIsMobile](#useismobile) - A boolean abstraction for mobile/desktop logic (below 768px; matches shadcn/ui behavior).
- [useIsTablet](#useistablet) - A boolean check for tablet and mobile devices (below 996px).

## When to use these hooks

**Real talk—this isn't for every responsive need.** CSS media queries (Tailwind classes like `md:block` or `hidden`) handle most styling cases perfectly and with better performance.

But when you need **responsive logic** in your React components or JavaScript behavior, these hooks deliver.

**Perfect for:**

- **Navigation patterns** - Switching between a Mobile Hamburger Menu and a Desktop Navigation Bar.
- **Conditional rendering** - Rendering entirely different component trees for mobile vs desktop.
- **Complex Interactions** - Disabling complex charts or animations on smaller screens.
- **Device detection** - Adjusting logic based on screen real estate.

## useViewport

The `useViewport` hook allows you to detect the current active breakpoint of the screen. It strictly mirrors the Tailwind CSS configuration defined in the project's theme.

### How it works

It returns the largest active breakpoint key (`'XS' | 'S' | 'M' | 'L' | 'XL'`) based on **min-width** media queries, matching how Tailwind's responsive prefixes (e.g., `md:`, `xl:`) work.

<TableRoot>
  <TableHeader>
    <TableRow>
      <TableHead>Breakpoint</TableHead>
      <TableHead>Min-Width</TableHead>
      <TableHead>Description</TableHead>
    </TableRow>
  </TableHeader>
  <TableBody>
    <TableRow>
      <TableCell className="font-bold">XL</TableCell>
      <TableCell>1200px</TableCell>
      <TableCell>Large desktops</TableCell>
    </TableRow>
    <TableRow>
      <TableCell className="font-bold">L</TableCell>
      <TableCell>996px</TableCell>
      <TableCell>Laptops / Desktops</TableCell>
    </TableRow>
    <TableRow>
      <TableCell className="font-bold">M</TableCell>
      <TableCell>768px</TableCell>
      <TableCell>Tablets</TableCell>
    </TableRow>
    <TableRow>
      <TableCell className="font-bold">S</TableCell>
      <TableCell>480px</TableCell>
      <TableCell>Large phones</TableCell>
    </TableRow>
    <TableRow>
      <TableCell className="font-bold">XS</TableCell>
      <TableCell>&lt; 480px</TableCell>
      <TableCell>Small phones</TableCell>
    </TableRow>
  </TableBody>
</TableRoot>

### Import

```tsx
import { useViewport } from '@chainlink/blocks'
```

### Usage

<Canvas of={MediaQueriesStories.UseViewport} />

```tsx
const ViewportVisualTest = () => {
  const viewport = useViewport()
  return (
    <div className="p-8 text-center">
      <Typography variant="h3">Current Viewport: {viewport}</Typography>
      <Typography variant="body-s" className="text-muted-foreground mt-2">
        Resize the window or use Storybook viewport toolbar to change.
      </Typography>
    </div>
  )
}
```

### SSR Fallback

To prevent hydration errors and layout flickering, the hook defaults to **'XL'** (Desktop) on the server. You can override this default if you are building a mobile-first page.

```tsx
// Default: assumes Desktop/XL on server (updates to actual viewport after hydration)
const viewport = useViewport()

// Custom default: assumes Mobile/XS on server (updates to actual viewport after hydration)
const viewport = useViewport('XS')
```

If you want to completely avoid any layout mismatches, you can pass `null` as the `serverFallback`. This will return `null` until the component hydrates, then update to the actual viewport value.

```tsx
const ResponsiveComponent = () => {
  const viewport = useViewport(null)

  // Don't render until we know the actual viewport
  if (viewport === null) {
    return <div>Loading...</div> // or return null
  }

  return <div>{viewport === 'XS' ? <MobileLayout /> : <DesktopLayout />}</div>
}
```

## useIsMobile

The `useIsMobile` hook is an abstraction designed to match the behavior of standard UI libraries (like shadcn/ui). It is useful for boolean checks where you need to switch between mobile and tablet/desktop layouts.

### How it works

Unlike `useViewport`, this hook uses a **max-width** media query of **767px**.

- Returns `true` if screen width is **< 768px** (Mobile)
- Returns `false` if screen width is **>= 768px** (Tablet/Desktop)

> **Note:** This breakpoint is exactly one pixel less than the `md` (768px) breakpoint. This ensures that `useIsMobile` covers everything strictly **below** the Tablet (`M`) breakpoint.

### Import

```tsx
import { useIsMobile } from '@chainlink/blocks'
```

### Usage

<Canvas of={MediaQueriesStories.UseIsMobile} />

```tsx
const IsMobileVisualTest = () => {
  const isMobile = useIsMobile()
  return (
    <div className="p-8 text-center">
      <Typography variant="h3">
        Is Mobile: {isMobile ? 'TRUE (Mobile)' : 'FALSE (Desktop)'}
      </Typography>
      <Typography variant="body-s" className="text-muted-foreground mt-2">
        This should be TRUE only below 768px.
      </Typography>
    </div>
  )
}
```

## useIsTablet

The `useIsTablet` hook returns `true` for tablet and mobile devices, and `false` for desktop. It's useful when you need to differentiate between desktop and everything else (tablets + mobile).

### How it works

This hook uses a **max-width** media query of **995px**.

- Returns `true` if screen width is **< 996px** (Tablet/Mobile)
- Returns `false` if screen width is **>= 996px** (Desktop)

> **Note:** This breakpoint is exactly one pixel less than the `lg` (996px) breakpoint. This ensures that `useIsTablet` covers everything strictly **below** the Desktop (`L`) breakpoint, including both tablets and mobile devices.

### Import

```tsx
import { useIsTablet } from '@chainlink/blocks'
```

### Usage

<Canvas of={MediaQueriesStories.UseIsTablet} />

```tsx
const IsTabletVisualTest = () => {
  const isTablet = useIsTablet()
  return (
    <div className="p-8 text-center">
      <Typography variant="h3">
        Is Tablet: {isTablet ? 'TRUE (Tablet/Mobile)' : 'FALSE (Desktop)'}
      </Typography>
      <Typography variant="body-s" className="text-muted-foreground mt-2">
        This should be TRUE below 996px (includes tablets and mobile).
      </Typography>
    </div>
  )
}
```

## Performance & Hydration

Both hooks use an optimized singleton listener pattern. Even if used in hundreds of components, only one event listener is attached to the window per unique query.

### Server-Side Rendering (SSR) Behavior

To prevent hydration mismatches and layout flickering for the majority of users, all hooks implement a **Desktop-First** strategy on the server:

1.  **useIsMobile**: Returns `false` (Desktop) on the server.
2.  **useIsTablet**: Returns `false` (Desktop) on the server.
3.  **useViewport**: Returns `XL` (Desktop) on the server.

This ensures that desktop users (who usually make up the majority of traffic for dashboards) see the correct layout immediately. Mobile and tablet users may see a brief layout adjustment upon hydration.
