import {
  Meta,
  Canvas,
  ArgsTable,
  Story,
  SourceState,
} from "@storybook/blocks";
import { ComponentHeading } from "../../storybook-components";
import { useDisclosure } from "../../hooks";
import { Button } from "../Button";
import { Flex } from "../Flex";
import { Panel } from "../Panel";
import { Grid } from "./Grid";

<Meta title="Components/Layout/Grid" component={Grid} />

<ComponentHeading
  componentName="Grid"
  description="A container to create layouts with CSS grid"
  sourcePath="src/components/Grid/Grid.tsx"
/>

`Grid` is just a `Box` with [grid layout](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout) enabled.

`Grid` also provides a `Grid.Item` child component. `Grid.Item` is not required
to use with `Grid`. Any child component of a `Grid` becomes a grid item.
`Grid.Item` just exposes some props to help you place the item within the grid.
Otherwise, you’ll likely need to apply a `style` prop to your grid items to
place them on rows or columns. There is an example below.

## Installation

```jsx
import { Grid } from "@conveyorhq/arrow-ds";
```

## Basic usage

<Canvas withSource={SourceState.OPEN}>
  <Story name="Grid">
    <Grid columns={["repeat(2, 1fr)"]} gap="24px">
      <Grid.Item column="1">
        <Panel className="p-4">Grid item</Panel>
      </Grid.Item>
      <Grid.Item column="2">
        <Panel className="p-4">Grid item</Panel>
      </Grid.Item>
    </Grid>
  </Story>
</Canvas>

## API

### Grid

Extends `BoxProps`

<ArgsTable of={Grid} />

### Grid.Item

Extends `BoxProps`

<ArgsTable of={Grid.Item} />

## Demos

### Explicit columns, implicit rows

CSS grid layout uses a concept of explicit and implicit grids. This is a
key concept that you should be aware of when creating a grid, otherwise
you could end up with rows or columns that you didn’t expect.

Explicit grids are created when you define the layout with the
`grid-template-rows`, `grid-template-columns`, and `grid-template-areas`
properties. Implicit grids are automatically generated by the grid
container whenever grid items are positioned outside of the explicit
grid.

You’ll notice that in the code example below, the columns are being set
explicitly `columns={["25%", "1fr"]}`, yet `rows` are not. Grid items
can still be given a row despite no rows being defined on the Grid
component itself. Here, the implicit rows use a track size of auto,
which is content-based, meaning the height of each row will vary to
accomodate the content inside of the grid item.

<Canvas withSource={SourceState.OPEN}>
  <Story name="Grid with explicit columns">
    <Grid columns={["25%", "1fr"]} gap="24px">
      <Grid.Item column="1 / -1" row="1">
        <Panel className="p-4">Grid item</Panel>
      </Grid.Item>
      <Grid.Item column="1 / 2" row="2">
        <Panel className="p-4">Grid item</Panel>
      </Grid.Item>
      <Grid.Item column="2 / -1" row="2">
        <Panel className="p-8">Grid item</Panel>
      </Grid.Item>
      <Grid.Item column="2 / -1" row="3">
        <Panel className="p-8">Grid item</Panel>
      </Grid.Item>
      <Grid.Item column="2 / -1" row="4">
        <Panel className="p-8">Grid item</Panel>
      </Grid.Item>
      <Grid.Item column="1 / -1" row="5">
        <Panel className="p-4">Grid item</Panel>
      </Grid.Item>
    </Grid>
  </Story>
</Canvas>

### Named gridlines

Sometimes it’s easier to place items on the grid when the rows or
columns are named. In this example, a two column layout is configured
and each of the grid areas have been named `sidebar` and `main`. Now
when placing the grid items, the name areas can be used instead of the
column numbers.

You can assign some or all of the lines in the grid a name when defining
the grid with the `grid-template-rows` and `grid-template-columns`
properties. The names can be anything you want, but the general
convention is to put the names inside square brackets.

<Canvas withSource={SourceState.OPEN}>
  <Story name="Grid with named gridlines">
    <Grid columns={["[sidebar] 25%", "[main] 1fr"]} gap="24px">
      <Grid.Item area="sidebar">
        <Panel className="p-8">Grid item</Panel>
      </Grid.Item>
      <Grid.Item area="main">
        <Panel className="p-8">Grid item</Panel>
      </Grid.Item>
    </Grid>
  </Story>
</Canvas>

### Spacing grid items

Use the `gap` property or gap [classnames](https://tailwindcss.com/docs/gap) to
set space between grid items. Gap can be set vertically, horizontally or both.

You can pass up to two values to gap; the first value being x-axis (row gap) add
the second value being y-axis (column gap).

Gap will not add space to the beginning or end of the list of grid items.

#### Equal gap on all sides

Use `gap="8px"` or `className="gap-2"`

<Canvas withSource={SourceState.OPEN}>
  <Story name="Grid with equal gap">
    <Grid columns="repeat(4, 1fr)" gap="8px">
      {[...Array(12).keys()].map((item) => {
        return (
          <Grid.Item key={`grid-item-${item}`}>
            <Panel className="p-8">Grid item {item + 1}</Panel>
          </Grid.Item>
        );
      })}
    </Grid>
  </Story>
</Canvas>

#### Unequal gap on all sides

Use `gap="16px 8px"` or `className="gap-x-4 gap-y-2"`

<Canvas withSource={SourceState.OPEN}>
  <Story name="Grid with unequal gap">
    <Grid columns="repeat(4, 1fr)" gap="16px 8px">
      {[...Array(12).keys()].map((item) => {
        return (
          <Grid.Item key={`grid-item-${item}`}>
            <Panel className="p-8">Grid item {item + 1}</Panel>
          </Grid.Item>
        );
      })}
    </Grid>
  </Story>
</Canvas>

#### Column gap

Use `gap="0 8px"` or `className="gap-y-2"`

<Canvas withSource={SourceState.OPEN}>
  <Story name="Grid with column gap">
    <Grid columns="repeat(4, 1fr)" gap="0 8px">
      {[...Array(12).keys()].map((item) => {
        return (
          <Grid.Item key={`grid-item-${item}`}>
            <Panel className="p-8">Grid item {item + 1}</Panel>
          </Grid.Item>
        );
      })}
    </Grid>
  </Story>
</Canvas>

#### Row gap

Use `gap="8px 0"` or `className="gap-x-2"`

<Canvas withSource={SourceState.OPEN}>
  <Story name="Grid with row gap">
    <Grid columns="repeat(4, 1fr)" gap="8px 0">
      {[...Array(12).keys()].map((item) => {
        return (
          <Grid.Item key={`grid-item-${item}`}>
            <Panel className="p-8">Grid item {item + 1}</Panel>
          </Grid.Item>
        );
      })}
    </Grid>
  </Story>
</Canvas>

### Using Grid.Item

`Grid.Item` provides props to help you place the item within the grid. These
props are not required nor always necessary to place items on the grid. If you
have a complex grid layout, you might want to be explicit in setting the row and
column for each item, otherwise the grid will place the items implicitly. In the
example below, Grid Item 4 does not define its column or row, but the grid
places the item in the first available spot on the grid. If we had 5 items and
one item did not define columns or rows, it would be placed outside of the grid
that was defined. You can try this below.

When a `column` or `row` value has two numbers divided by `/`, this is
shorthand for `grid-*-start` and `grid-*-end`.

When using `area`, values that are divided by `/`, this is shorthand for
`grid-row-start grid-column-start grid-row-end grid-column-end`.

A `grid-*-end` value of `-1` will extend the column full-width or row
full-height.

<Canvas withSource={SourceState.OPEN}>
  <Story name="Grid.Item">
    {() => {
      const { isOpen, onToggle } = useDisclosure();
      return (
        <>
          <Flex className="items-center gap-2 mb-4">
            <Button onClick={onToggle}>
              {isOpen ? "Remove" : "Add"} Grid Item 5
            </Button>
            {isOpen
              ? "That is because Grid Item 5 has explictly defined the space that Grid Item 4 implicitly occupies"
              : "This will push Grid Item 4 outside of the explict grid and create an implicit third row"}
          </Flex>
          <Grid columns="1fr 2fr 1fr" rows="repeat(2, 1fr)" gap="8px">
            <Grid.Item column="1 / 3" row="2 / auto">
              <Panel className="p-8">Grid item 1</Panel>
            </Grid.Item>
            <Grid.Item column="2" row="1">
              <Panel className="p-8">Grid item 2</Panel>
            </Grid.Item>
            <Grid.Item area="1 / 3 / -1 / -1">
              <Panel className="p-8 h-full">Grid item 3</Panel>
            </Grid.Item>
            <Grid.Item>
              <Panel
                className={`p-8 ${isOpen ? "bg-red-200" : "bg-green-200"}`}
              >
                Grid item 4
              </Panel>
            </Grid.Item>
            {isOpen && (
              <Grid.Item area="1 / 1 / 2 / 2">
                <Panel className="p-8 bg-gold-200">Grid item 5</Panel>
              </Grid.Item>
            )}
          </Grid>
        </>
      );
    }}
  </Story>
</Canvas>

### Opting out of Grid.Item

As stated above, `Grid.Item` is not required. Here’s how you might use
custom components inside of a grid.

<Canvas withSource={SourceState.OPEN}>
  <Story name="Grid without Grid.Item">
    <Grid columns="1fr 2fr 1fr" rows="repeat(2, 1fr)" gap="8px">
      <Panel
        className="p-8"
        style={{
          gridColumn: "3",
          gridRow: "2 / auto",
        }}
      >
        Grid item 1
      </Panel>
      <Panel
        className="p-8"
        style={{
          gridColumn: "2",
          gridRow: "2",
        }}
      >
        Grid item 2
      </Panel>
      <Panel
        className="p-8"
        style={{
          gridColumn: "1",
          gridRow: "1",
        }}
      >
        Grid item 3
      </Panel>
      <Panel className="p-8 h-full">Grid item 4</Panel>
    </Grid>
  </Story>
</Canvas>
