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

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

# EventItem Class

A powerful class for managing DOM events with TypeScript support, automatic element lifecycle tracking, and optimized event handlers. Provides a flexible API for working with events, including special handlers for resize and scroll.

## Key Features

- **Type-safe event management** — full TypeScript support with generics
- **Automatic DOM tracking** — events automatically disconnect when elements are removed from DOM
- **Multiple event types** — support for handling multiple event types simultaneously
- **Special handlers** — optimized handlers for resize (ResizeObserver) and scroll
- **Flexible state management** — easy enabling/disabling of events
- **Configurable options** — support for all standard addEventListener options
- **Event dispatching** — ability to programmatically trigger events
- **Element control** — separate element control for DOM state tracking

## Constructor

### `constructor`

Creates an EventItem instance with specified parameters.

**Parameters:**
- `elementSelector?: ElementOrString<E>` — element or selector to bind event to (optional)
- `type: string | string[]` — event type or array of types (defaults to ['click'])
- `listener?: EventListenerDetail<O, D>` — event handler function (optional)
- `options?: EventOptions` — options for addEventListener (optional)
- `detail?: D` — data to pass to the event (optional)

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

// Simple click event creation
const clickEvent = new EventItem(
  document.getElementById('button'),
  'click',
  (event, detail) => {
    console.log('Button clicked!', event, detail)
  }
)

// Multiple events
const multiEvent = new EventItem(
  '.interactive-element',
  ['mouseenter', 'mouseleave'],
  (event) => {
    console.log(`Event: ${event.type}`)
  }
)

// With additional data
const dataEvent = new EventItem(
  '#form',
  'submit',
  (event, detail) => {
    console.log('Form submitted:', detail)
  },
  { passive: false },
  { formId: 'contact-form', timestamp: Date.now() }
)
```

## State Query Methods

### `isActive`

Checks if event listening is currently active.

**Returns:** `boolean` — true if events are being listened to

```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`

Returns the element the event is bound to.

**Returns:** `E | undefined` — element or undefined

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

if (element) {
  console.log('Element found:', element.tagName)
} else {
  console.log('Element not found')
}
```

## Configuration Methods

### `setElement`

Changes the element for event tracking.

**Parameters:**
- `elementSelector?: ElementOrString<E>` — new element or selector

**Returns:** `this` — for method chaining

```javascript
const event = new EventItem()

// Set element
event.setElement('#button')
  .setType('click')
  .setListener(() => console.log('Click!'))
  .start()

// Change element
event.setElement('#another-button') // automatically restarts event
```

### `setElementControl`

Sets a separate element for DOM state control.

**Parameters:**
- `elementSelector?: ElementOrString<EC>` — element for control

**Returns:** `this` — for method chaining

```javascript
const event = new EventItem(window, 'resize', () => {
  console.log('Window resized')
})

// Control specific element presence
event.setElementControl('#main-container')
  .start()

// Event will automatically stop if #main-container is removed from DOM
```

### `setType`

Changes the type or types of events being handled.

**Parameters:**
- `type: string | string[]` — new event type or array of types

**Returns:** `this` — for method chaining

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

// Single event type
event.setType('click')

// Multiple types
event.setType(['mouseenter', 'mouseleave', 'focus', 'blur'])

// Changing type restarts active events
event.start()
event.setType('dblclick') // automatically restarts with new type
```

### `setListener`

Changes the event handler function.

**Parameters:**
- `listener: EventListenerDetail<O, D>` — new handler function

**Returns:** `this` — for method chaining

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

// Set first handler
event.setListener((event, detail) => {
  console.log('First handler')
})

// Replace handler
event.setListener((event, detail) => {
  console.log('New handler', detail)
})
```

### `setOptions`

Changes options for addEventListener.

**Parameters:**
- `options?: EventOptions` — new options

**Returns:** `this` — for method chaining

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

// Set options
event.setOptions({
  passive: true,
  capture: false
})

// Options for one-time event
event.setOptions({
  once: true,
  passive: false
})
```

### `setDetail`

Changes data passed to the event handler.

**Parameters:**
- `detail?: D` — new data

**Returns:** `this` — for method chaining

```javascript
const event = new EventItem('#form', 'submit', (event, detail) => {
  console.log('Form data:', detail)
})

// Set initial data
event.setDetail({
  formType: 'contact',
  validation: true
})

// Update data
event.setDetail({
  formType: 'newsletter',
  validation: false,
  timestamp: Date.now()
})
```

## Event Management

### `start`

Starts event listening.

**Returns:** `this` — for method chaining

```javascript
const event = new EventItem('#button', 'click', () => {
  console.log('Button clicked!')
})

// Start listening
event.start()

// Check state
console.log(event.isActive()) // true
```

### `stop`

Stops event listening.

**Returns:** `this` — for method chaining

```javascript
const event = new EventItem('#button', 'click', () => {
  console.log('This handler will no longer be called')
})

event.start()
// ... event is active ...
event.stop()

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

### `toggle`

Toggles event listening state.

**Parameters:**
- `activity: boolean` — true to start, false to stop

**Returns:** `this` — for method chaining

```javascript
const event = new EventItem('#toggle-btn', 'click', () => {
  console.log('Toggle!')
})

let isListening = false

// Toggle state
document.getElementById('control-btn').addEventListener('click', () => {
  isListening = !isListening
  event.toggle(isListening)
  console.log('Listening:', isListening)
})
```

### `reset`

Restarts event listening (stops and starts again).

**Returns:** `this` — for method chaining

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

event.start()

// Changing settings requires restart
event.setOptions({ passive: true })
  .reset() // Applies new options
```

### `dispatch`

Programmatically triggers the event.

**Parameters:**
- `detail?: D` — data for the event (defaults to this.detail)

**Returns:** `this` — for method chaining

```javascript
const event = new EventItem('#button', 'custom-event', (event, detail) => {
  console.log('Custom event:', detail)
})

event.start()

// Programmatic event trigger
event.dispatch({
  message: 'Hello from code!',
  timestamp: Date.now()
})

// Or with new data
event.dispatch({
  message: 'Different message',
  source: 'manual'
})
```

## Special Event Types

### `resize` Event

EventItem automatically uses ResizeObserver for optimized element resize tracking.

```javascript
const resizeEvent = new EventItem('#resizable-element', 'resize', (entry) => {
  console.log('New dimensions:', {
    width: entry.contentRect.width,
    height: entry.contentRect.height
  })
})

resizeEvent.start()

// ResizeObserver will be used automatically for better performance
```

### `scroll-sync` Event

Special event for synchronized scroll with requestAnimationFrame optimization.

```javascript
const scrollEvent = new EventItem('#scrollable-container', 'scroll-sync', () => {
  console.log('Synchronized scroll')
})

scrollEvent.start()

// Handler will be called no more than once per animation frame
```

## Practical Examples

### Modal Management

```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()
  }
}

// Usage
const modal = new ModalManager()
document.getElementById('open-btn').addEventListener('click', () => {
  modal.openModal(document.getElementById('modal'))
})
```

### Drag and Drop System

```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()
  }
}

// Usage
const dragDrop = new DragDropManager()
dragDrop.makeDraggable(document.getElementById('item'), { id: '1', type: 'product' })
dragDrop.createDropZone(document.getElementById('basket'), (data) => {
  console.log('Added to basket:', data)
})
```

The EventItem class provides a powerful and flexible tool for managing DOM events in JavaScript applications, ensuring type safety, automatic lifecycle management, and optimized performance.
