
# Утиліти для класів

Створення атрибутів `class` у шаблонах передбачає об’єднання рядків, перемикання класів за умовами й запобігання конфліктам між пізнішими та ранішими утилітами (`p-2` і `p-4` не мають одночасно залишатися). У JavaScript ці завдання розподілено між [clsx](https://github.com/lukeed/clsx) і [tailwind-merge](https://github.com/dcastil/tailwind-merge). TailwindPHP містить порти PHP обох бібліотек, тож ви отримуєте таку саму поведінку без інструментів Node.js і додаткових пакетів Composer.

Три функції охоплюють усі потреби: `cn()` створює та усуває конфлікти, `merge()` лише усуває конфлікти, а `join()` лише об’єднує рядки.

## cn()

Рекомендована утиліта. Спочатку вона формує класи умовно, як clsx, а потім розв’язує конфлікти Tailwind за допомогою tailwind-merge — перемагає остання конфліктна утиліта.

```php
use function TailwindPHP\cn;

// Conflicts are resolved — px-4 overrides px-2
cn('px-2 py-1', 'px-4');
// => 'py-1 px-4'

// Conditional classes via a key => bool array
cn('btn', ['btn-primary' => true, 'btn-disabled' => false]);
// => 'btn btn-primary'
```

### Допустимі аргументи

`cn()` приймає будь-яку кількість аргументів. Кожен розгортається та фільтрується за семантикою clsx:

| Аргумент | Поведінка |
|----------|----------|
| `string` | Додається без змін. |
| `int` / `float` | Перетворюється на рядок і додається. |
| Послідовний масив | Кожен елемент обробляється рекурсивно. |
| Масив `key => bool` | Ключ додається, якщо його значення істинне. |
| `null`, `false`, `0`, `''` | Ігноруються. |

```php
cn('flex', ['items-center' => true], ['p-4', ['gap-2']], null);
// => 'flex items-center p-4 gap-2'
```

### Шаблон компонента

`cn()` дає змогу перевизначати компонент: об’єднайте власні класи компонента з переданими викликачем, і конфлікти розв’язуватимуться на його користь.

```php
use function TailwindPHP\cn;

function Card(array $props = []): string {
    $class = cn(
        'rounded-lg border bg-card text-card-foreground shadow-sm',
        $props['class'] ?? null
    );
    return "<div class=\"{$class}\">" . ($props['children'] ?? '') . "</div>";
}

Card(['class' => 'p-6', 'children' => 'Content']);
// <div class="rounded-lg border bg-card text-card-foreground shadow-sm p-6">Content</div>
```

Для декларативних стилів компонентів із кількома осями поєднуйте `cn()` з [variants()](https://tailwindphp.inline0.dev/uk/docs/class-utilities/variants).

## merge()

Лише розв’язання конфліктів, без умовного створення. Передавайте рядки класів; пізніші класи перевизначають раніші, якщо задають ту саму властивість. Використовуйте цю функцію, коли вхідні дані вже є звичайними рядками.

```php
use function TailwindPHP\merge;

merge('px-2 py-1 bg-red-500', 'px-4 bg-blue-500');
// => 'py-1 px-4 bg-blue-500'

// Conflict resolution is variant-aware
merge('hover:bg-red-500', 'hover:bg-blue-500');
// => 'hover:bg-blue-500'
```

`cn()` побудовано на основі clsx за допомогою `merge()`, тож використовуйте безпосередньо `merge()` лише тоді, коли вам не потрібен умовний синтаксис із масивами.

## join()

Звичайне об’єднання без розв’язання конфліктів. Значення, які оцінюються як хибні, відкидаються, але конфліктні утиліти залишаються. Використовуйте цю функцію, коли конфліктів не буде й потрібен найекономніший варіант.

```php
use function TailwindPHP\join;

join('foo', 'bar', null, 'baz');
// => 'foo bar baz'
```

## Що вибрати?

| Функція | Створює (умовні класи, масиви) | Розв’язує конфлікти | Коли використовувати |
|----------|:---:|:---:|----------|
| `cn()`    | так | так | Типовий варіант — для рядків класів, у яких можливі конфлікти. |
| `merge()` | ні  | так | Вхідні дані — звичайні рядки; потрібно лише усунути конфлікти. |
| `join()`  | так | ні  | Ви знаєте, що конфліктів не буде, і хочете мінімізувати обсяг роботи. |

## Імпорт

```php
use function TailwindPHP\cn;
use function TailwindPHP\merge;
use function TailwindPHP\join;
```

Усі три функції містяться в основному просторі імен `TailwindPHP` та входять до пакета. Це PHP порти clsx (автор — Luke Edwards) і tailwind-merge (автор — Dany Castillo).
