import { Button, Form, Switch } from '@fluid-design/fluid-ui';

import { ButtonExamples, MDXLayoutNext } from '@/components';

export const meta = {
  templateTitle: 'Button',
  description:
    'Learn how to create beautiful and functional buttons using Fluid Design. Discover all the features and options available to enhance the user experience.',
  date: '2023-02-01',
  author: 'Oliver Pan',
  tags: [
    'fluid ui',
    'react',
    'framer motion',
    'headless ui',
    'tailwindcss',
    'button',
  ],
};

export default (props) => (
  <MDXLayoutNext
    meta={meta}
    components={{
      Button,
      Form,
      Switch,
      ...ButtonExamples,
    }}
    {...props}
  />
);

# Button

## Colors

Using the TailwindCSS Fluid UI plugin, you can easily use button component with all built-in support for:

<CodeFrame title='Color options'>
  <div className='flex flex-wrap justify-center gap-6'>
    <ButtonExamples.Colors />
  </div>
</CodeFrame>

```jsx
import { Button } from '@fluid-design/fluid-ui';

function Example() {
  return (
    // mark[13:23]
    <Button color='red'>Red</Button>
    // Use `label` prop for automatic screen reader support
    <Button color='red' label='Red' />
  );
}
```

### Custom Colors

With the v2.0.0 update, you can now create custom colors using className combined with TailwindCSS variant conventions.
The buttons will automatically be styled with the correct color and hover state, as well as a11y support.
**Note** if you specified button color using className, e.g. `className='bg-red-500'` or `className='btn-blue-500'` `'hover:btn-outline-blue-600'`, the `color` prop will be ignored.
And the custom color will be preseved. Based on the weight styles, the preseved color will either be text color or background color.
For a11y support, it will try to generate contrast colors, but you might need to handle a11y yourself.

<CodeFrame title='Color options'>
  <div className='flex flex-wrap justify-center gap-6'>
    <ButtonExamples.CustomColors />
  </div>
</CodeFrame>

```jsx
<Button className='btn-[olive]' label='Olive' />
<Button className='btn-light-[#556B2F]' label='#556B2F' />
<Button className='btn-outline-[#556B2F]/80' label='Opacity' />
<Button className='btn-clear-[rgb(85,107,47)]' label='RGB' />
```

## Weight

Button weight defines the boldness of the button.

<CodeFrame title='Weight options'>
  <div className='flex flex-wrap justify-center gap-6'>
    <ButtonExamples.Weights />
  </div>
</CodeFrame>

```jsx
<Button color="rose" weight="light">Light</Button>
<Button color="rose" weight="normal">Normal</Button>
<Button color="rose" weight="bold">Bold</Button>
<Button color="rose" weight="outline">Outline</Button>
<Button color="rose" weight="clear">Clear</Button>
<Button color="rose" weight="link">Link</Button>
```

## Size

Button size defines the size of the button.

<CodeFrame title='Size options'>
  <div className='flex flex-wrap justify-center gap-6'>
    <ButtonExamples.Sizes />
  </div>
</CodeFrame>

```jsx
<Button color="indigo" size="xs">Extra Small</Button>
<Button color="indigo" size="sm">Small</Button>
<Button color="indigo" size="md">Medium</Button>
<Button color="indigo" size="lg">Large</Button>
<Button color="indigo" size="xl">Extra Large</Button>
```

## States

Button states defines the different states of the button.
You can use the `isLoading` prop to show a loading state.
And customize the loading state by passing the `loadingOptions` prop.

<CodeFrame title='Size options'>
  <div className='flex flex-wrap justify-center gap-6'>
    <ButtonExamples.States />
  </div>
</CodeFrame>

```jsx
<Button color="lime" disabled>
  Disabled
</Button>
<Button
  color='green'
  disabled={disabled}
  isLoading={isLoading}
>
  Loading
</Button>
<Button
  color='green'
  disabled={disabled}
  isLoading={isLoading}
  loadingOptions={{
    animation: 'spin-large',
  }}
>
  Send
</Button>
<Button
  color='green'
  disabled={disabled}
  isLoading={isLoading}
  loadingOptions={{
    animation: 'pulse',
  }}
>
  Submit
</Button>
<Button
  color='green'
  disabled={disabled}
  isLoading={isLoading}
  loadingOptions={{
    animation: 'ping',
    text: 'Loading',
  }}
>
  Confirm
</Button>
```

## Icon

Fluid UI Button provides many ways to add and customize icons.

For a text button, you can add icon using `icon`, `iconStart` and `iconEnd` props.
For an icon-only button, you can use any of the props to add an icon.

### Icon with text

You can add icon as a function or a React Element,
if you add it as a function, the styles will be applied to the icon matching the current button style.
You can overwrite the default style by adding `iconClassName` prop.
Or if you need more customization, you can add any React Element to the `icon` props.

<CodeFrame title='Text icon example'>
  <div className='flex flex-wrap justify-center gap-6'>
    <ButtonExamples.IconWithText />
  </div>
</CodeFrame>

```jsx
<Button
  color='green'
  icon={MdAddCircle}
  label='Create'
/>
<Button
  color='sky'
  shape='square'
>
  <MdSend />
  <span>Send Email</span>
</Button>
<Button
  color='blue'
  shape='pill'
  weight='light'
  iconStart={MdInfo}
  iconEnd={<MdChevronRight className='w-5 h-5 rtl:rotate-180' />}
  iconEndPosition='between'
  label='Info'
/>
<Button
  color='rose'
  shape='pill'
  weight='clear'
  iconStart={IoMdTrash}
>
  <span>Delete</span>
</Button>
```

### Icon only

The component also provides an `IconOnly` prop that will render the button paddings the same for all edges.
If you provided `label` prop, the text will hide from HTML but still be accessible to screen readers.

<CodeFrame title='Icon only'>
  <div className='flex flex-wrap justify-center gap-6'>
    <ButtonExamples.IconOnly />
  </div>
</CodeFrame>

```jsx
<Button
  label='Create'
  color='green'
  icon={MdAddCircle}
  iconOnly
/>
<Button
  label='Send'
  color='sky'
  shape='square'
  iconOnly
>
  <MdSend />
</Button>
<Button
  label='Info'
  color='blue'
  shape='pill'
  weight='light'
  iconOnly
>
  <MdInfo />
</Button>
<Button
  label='Delete'
  color='rose'
  shape='pill'
  weight='clear'
  icon={<IoMdTrash />}
  iconOnly
/>
```

## Features

### Accessibility

The button can be keyboard focused with border color matches the color of the text.
In high contrast mode, the button contrast and font weight will increase while preserving the original color scheme.
If a label is provided, the button will be accessible to screen readers.
To customize the screen reader text, you can use the `sr` prop.

```jsx
<Button
  label='Create'
  // mark
  sr='Create a new post'
/>
```

### Forground Color

Based on contrast ratio, each button's foreground color is set to the opposite of the background color.
In short, the text color will be > 4.5:1 contrast ratio with the background color.
And in high contrast mode, the text color will be > 7:1 contrast ratio with the background color.
Read more about accessible color contrast <ExternalLink href='https://www.w3.org/TR/WCAG20-TECHS/G18.html'>here</ExternalLink>.

### Button States

Then button will only be hoverable or clickable when enabled. This is done by utilizing TailwindCSS's `:enabled` prop.
For disabled situation: A cursor not-allowed will be displayed. As well as dimming the button brightness.
Hover and focus-visible are being paried to make the button more accessible.
Active state will either lighten or darken the button color based on the button's weight and color.

### Loading animation

When loading is true, a spinner will be shown. Customizable loading animation.
It is also possible to set `isLoading` to true to display a loading animation,
and it can be grouped along with disabled to create a funtional loading button.

## Component API

```jsx
import { Button } from "@fluid-design/fluid-ui";

<Button weight="normal"
  size="md"
  shape="round"
  isLoading={false}
  loadingOptions={
    animation: "spin",
    text: ""
  }>
  Text
</Button>
```

| Prop                | Default     | Description                                                                                                                                                                                                                         |
| ------------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `as`                | `div`       | `String \| Component` <Table.D>The element or component the `Button` should render as.</Table.D>                                                                                                                                    |
| `badgeClassName`    | `undefined` | `String` <Table.D>The class name of the badge.</Table.D>                                                                                                                                                                            |
| `badge`             | `undefined` | `String \| React.ReactNode` <Table.D>The badge content.</Table.D>                                                                                                                                                                   |
| `className`         | `undefined` | `String` <Table.D>The class name of the button.</Table.D>                                                                                                                                                                           |
| `color`             |             | `red`, `orange`, `amber`, `yellow`, `lime`, `green`, `emerald`, `teal`, `cyan`, `sky`, `blue`, `indigo`, `violet`, `purple`, `fuchsia`, `pink`, `rose`, `gray`, `slate`, `zinc`, `neutral`, `stone` <Table.D>Button color</Table.D> |
| `iconClassName`     |             | `String` <Table.D>The class name of the icon.</Table.D>                                                                                                                                                                             |
| `iconEndPosition`   | `flex`      | `flex \| between` <Table.D>The position alignment of the icon end.</Table.D>                                                                                                                                                        |
| `iconEnd`           |             | `Function \| React.ReactNode` <Table.D>The icon end content.</Table.D>                                                                                                                                                              |
| `iconOnly`          |             | `boolean` <Table.D>Adjust the padding to be the same for all edges</Table.D>                                                                                                                                                        |
| `iconStartPosition` | `flex`      | `flex \| between` <Table.D>The position alignment of the icon start.</Table.D>                                                                                                                                                      |
| `iconStart`         |             | `Function \| React.ReactNode` <Table.D>The icon start content.</Table.D>                                                                                                                                                            |
| `icon`              |             | `Function \| React.ReactNode` <Table.D>The icon content. It will always comes after the text element, if any.</Table.D>                                                                                                             |
| `isLoaded`          |             | `boolean` <Table.D>If true, it will trigger a loaded animation with default timeout. You'll need to manually set it back to `false` if you want to trigger it multiple times. </Table.D>                                            |
| `loadedOptions`     |             | `Object` <Table.D>The options for the loaded animation.</Table.D> <pre>text?: string \| undefined;<br />icon?: React.ReactNode \| \{ \(props\): JSX.Element \};duration?: number; // default 1500ms<br />className?: string;</pre>  |
| `isLoading`         | `false`     | `boolean` <Table.D>Whether the button is loading</Table.D>                                                                                                                                                                          |
| `loadingOptions`    | -           | `object` <Table.D>Loading animation options</Table.D> <pre>animation?: 'spin'\| 'pulse'\| 'ping'\| 'spin-large' <br />text?: string;</pre>                                                                                          |
| `shape`             | `rounded`   | `pill`, `rounded`, `square` <Table.D>Button shape</Table.D>                                                                                                                                                                         |
| `size`              | `md`        | `xs`, `sm`, `md`, `lg`, `xl` <Table.D>Button size</Table.D>                                                                                                                                                                         |
| `sr`                |             | `String` <Table.D>Screen reader text. This will be overwrite `label` prop if provided.</Table.D>                                                                                                                                    |
| `weight`            | `normal`    | `light`, `normal`, `bold`, `outline`, `clear`, `link` <Table.D>Button weight</Table.D>                                                                                                                                              |
