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

<Meta title='@dxtmisha/functional/ru/Classes/EventItem'/>

# Класс EventItem

Мощный класс для управления событиями DOM с поддержкой TypeScript, автоматическим отслеживанием жизненного цикла элементов и оптимизированными обработчиками событий. Предоставляет гибкий API для работы с событиями, включая специальные обработчики для resize и scroll.

## Основные возможности

- **Типобезопасное управление событиями** — полная поддержка TypeScript с дженериками
- **Автоматическое отслеживание DOM** — события автоматически отключаются при удалении элементов из DOM
- **Множественные типы событий** — поддержка обработки нескольких типов событий одновременно
- **Специальные обработчики** — оптимизированные обработчики для resize (ResizeObserver) и scroll
- **Гибкое управление состоянием** — легкое включение/выключение событий
- **Настраиваемые опции** — поддержка всех стандартных опций addEventListener
- **Диспетчеризация событий** — возможность программного вызова событий
- **Контроль элементов** — отдельный контроль элемента для отслеживания состояния DOM

## Конструктор

### `constructor`

Создаёт экземпляр EventItem с указанными параметрами.

**Параметры:**
- `elementSelector?: ElementOrString<E>` — элемент или селектор для привязки события (опционально)
- `type: string | string[]` — тип события или массив типов (по умолчанию ['click'])
- `listener?: EventListenerDetail<O, D>` — функция-обработчик события (опционально)
- `options?: EventOptions` — опции для addEventListener (опционально)
- `detail?: D` — данные для передачи в событие (опционально)

```javascript
import { EventItem } from '@dxtmisha/functional'

// Простое создание события клика
const clickEvent = new EventItem(
  document.getElementById('button'),
  'click',
  (event, detail) => {
    console.log('Кнопка нажата!', event, detail)
  }
)

// Множественные события
const multiEvent = new EventItem(
  '.interactive-element',
  ['mouseenter', 'mouseleave'],
  (event) => {
    console.log(`Событие: ${event.type}`)
  }
)

// С дополнительными данными
const dataEvent = new EventItem(
  '#form',
  'submit',
  (event, detail) => {
    console.log('Форма отправлена:', detail)
  },
  { passive: false },
  { formId: 'contact-form', timestamp: Date.now() }
)
```

## Методы получения состояния

### `isActive`

Проверяет, активно ли прослушивание событий в данный момент.

**Возвращает:** `boolean` — true если события прослушиваются

```javascript
const event = new EventItem('#button', 'click', () => {})

console.log(event.isActive()) // false

event.start()
console.log(event.isActive()) // true

event.stop()
console.log(event.isActive()) // false
```

### `getElement`

Возвращает элемент, к которому привязано событие.

**Возвращает:** `E | undefined` — элемент или undefined

```javascript
const event = new EventItem('#button', 'click', () => {})
const element = event.getElement()

if (element) {
  console.log('Элемент найден:', element.tagName)
} else {
  console.log('Элемент не найден')
}
```

## Методы настройки

### `setElement`

Изменяет элемент для отслеживания событий.

**Параметры:**
- `elementSelector?: ElementOrString<E>` — новый элемент или селектор

**Возвращает:** `this` — для цепочки вызовов

```javascript
const event = new EventItem()

// Установка элемента
event.setElement('#button')
  .setType('click')
  .setListener(() => console.log('Клик!'))
  .start()

// Изменение элемента
event.setElement('#another-button') // автоматически перезапускает событие
```

### `setElementControl`

Устанавливает отдельный элемент для контроля состояния DOM.

**Параметры:**
- `elementSelector?: ElementOrString<EC>` — элемент для контроля

**Возвращает:** `this` — для цепочки вызовов

```javascript
const event = new EventItem(window, 'resize', () => {
  console.log('Размер окна изменился')
})

// Контролировать наличие специфического элемента
event.setElementControl('#main-container')
  .start()

// Событие автоматически остановится, если #main-container будет удален из DOM
```

### `setType`

Изменяет тип или типы обрабатываемых событий.

**Параметры:**
- `type: string | string[]` — новый тип события или массив типов

**Возвращает:** `this` — для цепочки вызовов

```javascript
const event = new EventItem('#element')

// Один тип события
event.setType('click')

// Множественные типы
event.setType(['mouseenter', 'mouseleave', 'focus', 'blur'])

// Изменение типа перезапускает активные события
event.start()
event.setType('dblclick') // автоматически перезапускается с новым типом
```

### `setListener`

Изменяет функцию-обработчик события.

**Параметры:**
- `listener: EventListenerDetail<O, D>` — новая функция-обработчик

**Возвращает:** `this` — для цепочки вызовов

```javascript
const event = new EventItem('#button', 'click')

// Установка первого обработчика
event.setListener((event, detail) => {
  console.log('Первый обработчик')
})

// Замена обработчика
event.setListener((event, detail) => {
  console.log('Новый обработчик', detail)
})
```

### `setOptions`

Изменяет опции для addEventListener.

**Параметры:**
- `options?: EventOptions` — новые опции

**Возвращает:** `this` — для цепочки вызовов

```javascript
const event = new EventItem('#element', 'scroll')

// Установка опций
event.setOptions({
  passive: true,
  capture: false
})

// Опции для одноразового события
event.setOptions({
  once: true,
  passive: false
})
```

### `setDetail`

Изменяет данные, передаваемые в обработчик события.

**Параметры:**
- `detail?: D` — новые данные

**Возвращает:** `this` — для цепочки вызовов

```javascript
const event = new EventItem('#form', 'submit', (event, detail) => {
  console.log('Данные формы:', detail)
})

// Установка начальных данных
event.setDetail({
  formType: 'contact',
  validation: true
})

// Обновление данных
event.setDetail({
  formType: 'newsletter',
  validation: false,
  timestamp: Date.now()
})
```

## Управление событиями

### `start`

Запускает прослушивание событий.

**Возвращает:** `this` — для цепочки вызовов

```javascript
const event = new EventItem('#button', 'click', () => {
  console.log('Кнопка нажата!')
})

// Запуск прослушивания
event.start()

// Проверка состояния
console.log(event.isActive()) // true
```

### `stop`

Останавливает прослушивание событий.

**Возвращает:** `this` — для цепочки вызовов

```javascript
const event = new EventItem('#button', 'click', () => {
  console.log('Этот обработчик больше не вызывается')
})

event.start()
// ... событие активно ...
event.stop()

console.log(event.isActive()) // false
```

### `toggle`

Переключает состояние прослушивания событий.

**Параметры:**
- `activity: boolean` — true для запуска, false для остановки

**Возвращает:** `this` — для цепочки вызовов

```javascript
const event = new EventItem('#toggle-btn', 'click', () => {
  console.log('Переключатель!')
})

let isListening = false

// Переключение состояния
document.getElementById('control-btn').addEventListener('click', () => {
  isListening = !isListening
  event.toggle(isListening)
  console.log('Прослушивание:', isListening)
})
```

### `reset`

Перезапускает прослушивание событий (останавливает и запускает заново).

**Возвращает:** `this` — для цепочки вызовов

```javascript
const event = new EventItem('#element', 'click', () => {
  console.log('Клик!')
})

event.start()

// Изменение настроек требует перезапуска
event.setOptions({ passive: true })
  .reset() // Применяет новые опции
```

### `dispatch`

Программно запускает событие.

**Параметры:**
- `detail?: D` — данные для события (по умолчанию используется this.detail)

**Возвращает:** `this` — для цепочки вызовов

```javascript
const event = new EventItem('#button', 'custom-event', (event, detail) => {
  console.log('Пользовательское событие:', detail)
})

event.start()

// Программный вызов события
event.dispatch({
  message: 'Привет из кода!',
  timestamp: Date.now()
})

// Или с новыми данными
event.dispatch({
  message: 'Другое сообщение',
  source: 'manual'
})
```

## Специальные типы событий

### Событие `resize`

EventItem автоматически использует ResizeObserver для оптимизированного отслеживания изменений размера элементов.

```javascript
const resizeEvent = new EventItem('#resizable-element', 'resize', (entry) => {
  console.log('Новые размеры:', {
    width: entry.contentRect.width,
    height: entry.contentRect.height
  })
})

resizeEvent.start()

// ResizeObserver будет использован автоматически для лучшей производительности
```

### Событие `scroll-sync`

Специальное событие для синхронизированного скролла с оптимизацией через requestAnimationFrame.

```javascript
const scrollEvent = new EventItem('#scrollable-container', 'scroll-sync', () => {
  console.log('Синхронизированный скролл')
})

scrollEvent.start()

// Обработчик будет вызываться не чаще одного раза за кадр анимации
```

## Практические примеры

### Управление модальными окнами

```javascript
class ModalManager {
  constructor() {
    this.overlayEvent = null
    this.escapeEvent = null
  }

  openModal(element) {
    element.classList.add('modal-open')

    this.overlayEvent = new EventItem(document, 'click', (event) => {
      if (event.target.classList.contains('modal-overlay')) {
        this.closeModal(element)
      }
    })

    this.escapeEvent = new EventItem(document, 'keydown', (event) => {
      if (event.key === 'Escape') this.closeModal(element)
    })

    this.overlayEvent.start()
    this.escapeEvent.start()
  }

  closeModal(element) {
    element.classList.remove('modal-open')
    this.overlayEvent?.stop()
    this.escapeEvent?.stop()
  }
}

// Использование
const modal = new ModalManager()
document.getElementById('open-btn').addEventListener('click', () => {
  modal.openModal(document.getElementById('modal'))
})
```

### Drag and Drop система

```javascript
class DragDropManager {
  makeDraggable(element, data = {}) {
    const dragStart = new EventItem(element, 'dragstart', (event) => {
      event.dataTransfer.setData('text/plain', JSON.stringify(data))
      element.classList.add('dragging')
    })

    const dragEnd = new EventItem(element, 'dragend', () => {
      element.classList.remove('dragging')
    })

    element.draggable = true
    dragStart.start()
    dragEnd.start()
  }

  createDropZone(element, onDrop) {
    const dropEvent = new EventItem(element, 'drop', (event) => {
      event.preventDefault()
      const data = JSON.parse(event.dataTransfer.getData('text/plain'))
      onDrop(data)
    }, { passive: false })

    const dragOver = new EventItem(element, 'dragover', (event) => {
      event.preventDefault()
    }, { passive: false })

    dropEvent.start()
    dragOver.start()
  }
}

// Использование
const dragDrop = new DragDropManager()
dragDrop.makeDraggable(document.getElementById('item'), { id: '1', type: 'product' })
dragDrop.createDropZone(document.getElementById('basket'), (data) => {
  console.log('Добавлен в корзину:', data)
})
```

Класс EventItem предоставляет мощный и гибкий инструмент для управления событиями DOM в JavaScript-приложениях, обеспечивая типобезопасность, автоматическое управление жизненным циклом и оптимизированную производительность.
