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

<Meta title='@dxtmisha/functional/ru/Composables/useLazyRef'/>

# Композабл useLazyRef

Композабл для отслеживания появления элементов на экране с использованием Intersection Observer API. Обеспечивает эффективную ленивую загрузку и рендеринг компонентов только когда они становятся видимыми в области просмотра. Идеально подходит для оптимизации производительности при работе с длинными списками, изображениями и тяжёлыми компонентами.

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

- **Отслеживание видимости** — автоматическое определение когда элемент появляется в области просмотра
- **Intersection Observer API** — использует нативный браузерный API для эффективного отслеживания
- **Автоматическая очистка** — удаление наблюдателей при размонтировании компонентов
- **Реактивный статус** — возвращает реактивную переменную с состоянием видимости
- **Настраиваемые границы** — поддержка rootMargin для предзагрузки контента
- **Множественные элементы** — управление отслеживанием нескольких элементов одновременно
- **Graceful degradation** — работает без IntersectionObserver в старых браузерах

## Функция

### `useLazyRef`

Создаёт экземпляр для отслеживания видимости элементов на экране.

**Параметры:** нет

**Возвращает:** объект с методами:
- `intersectionObserver: IntersectionObserver | undefined` — экземпляр наблюдателя
- `addLazyItem: (element: Ref<HTMLElement>) => ShallowRef<boolean>` — добавить элемент для отслеживания
- `removeLazyItem: (element?: HTMLElement) => void` — удалить элемент из отслеживания
- `disconnectLazy: () => void` — отключить все наблюдатели

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

// Создание экземпляра
const lazy = useLazyRef()

// Доступ к методам
const isVisible = lazy.addLazyItem(elementRef)
lazy.removeLazyItem(element)
lazy.disconnectLazy()
```

## Основное использование

### `addLazyItem`

Добавляет элемент для отслеживания его видимости на экране.

**Параметры:**
- `element: Ref<HTMLElement>` — реактивная ссылка на HTML элемент

**Возвращает:** `ShallowRef<boolean>` — реактивная переменная со статусом видимости

```javascript
import { ref } from 'vue'
import { useLazyRef } from '@dxtmisha/functional'

const lazy = useLazyRef()
const imageRef = ref(null)

// Добавление элемента для отслеживания
const isVisible = lazy.addLazyItem(imageRef)

// isVisible.value === true когда элемент видим
// isVisible.value === false когда элемент невидим

// В старых браузерах без IntersectionObserver
// isVisible.value === true сразу (fallback)
```

### `removeLazyItem`

Удаляет элемент из отслеживания и очищает ресурсы.

**Параметры:**
- `element?: HTMLElement` — HTML элемент для удаления

**Возвращает:** `void`

```javascript
const element = document.getElementById('my-element')

// Удаление элемента из отслеживания
lazy.removeLazyItem(element)
```

### `disconnectLazy`

Отключает все наблюдатели и прекращает отслеживание всех элементов.

**Параметры:** нет

**Возвращает:** `void`

```javascript
// Полная очистка при размонтировании
onBeforeUnmount(() => {
  lazy.disconnectLazy()
})
```

## Использование в компонентах

### Ленивая загрузка изображений

```javascript
import { ref, watch } from 'vue'
import { useLazyRef } from '@dxtmisha/functional'

export default {
  setup() {
    const lazy = useLazyRef()
    const imageRef = ref(null)
    const imageLoaded = ref(false)

    const isVisible = lazy.addLazyItem(imageRef)

    watch(isVisible, (visible) => {
      if (visible && !imageLoaded.value) {
        imageLoaded.value = true
      }
    })

    return { imageRef, imageLoaded }
  }
}

// Template:
// <div ref="imageRef">
//   <img v-if="imageLoaded" src="/path/to/image.jpg" />
//   <div v-else class="placeholder">Загрузка...</div>
// </div>
```

### Бесконечная прокрутка

```javascript
import { ref, watch } from 'vue'
import { useLazyRef } from '@dxtmisha/functional'

export default {
  setup() {
    const lazy = useLazyRef()
    const items = ref([1, 2, 3, 4, 5])
    const sentinelRef = ref(null)
    const loading = ref(false)

    const isSentinelVisible = lazy.addLazyItem(sentinelRef)

    watch(isSentinelVisible, async (visible) => {
      if (visible && !loading.value) {
        loading.value = true
        await new Promise(resolve => setTimeout(resolve, 1000))
        items.value.push(items.value.length + 1)
        loading.value = false
      }
    })

    return { items, sentinelRef, loading }
  }
}

// Template:
// <div>
//   <div v-for="item in items" :key="item">Элемент {{ item }}</div>
//   <div ref="sentinelRef" class="sentinel">
//     <div v-if="loading">Загрузка...</div>
//   </div>
// </div>
```

## Продвинутое использование

### Отслеживание множества элементов

```javascript
import { ref, onBeforeUnmount } from 'vue'
import { useLazyRef } from '@dxtmisha/functional'

export default {
  setup() {
    const lazy = useLazyRef()
    const items = ref([
      { id: 1, ref: ref(null), visible: null },
      { id: 2, ref: ref(null), visible: null },
      { id: 3, ref: ref(null), visible: null }
    ])

    // Добавление каждого элемента для отслеживания
    items.value.forEach(item => {
      item.visible = lazy.addLazyItem(item.ref)
    })

    onBeforeUnmount(() => {
      lazy.disconnectLazy()
    })

    return { items }
  }
}

// Template:
// <div v-for="item in items" :key="item.id">
//   <div :ref="item.ref">
//     <div v-if="item.visible.value">
//       Контент элемента {{ item.id }}
//     </div>
//     <div v-else class="placeholder">...</div>
//   </div>
// </div>
```

### Условное удаление элементов

```javascript
import { ref, watch } from 'vue'
import { useLazyRef } from '@dxtmisha/functional'

export default {
  setup() {
    const lazy = useLazyRef()
    const elementRef = ref(null)
    const showElement = ref(true)

    const isVisible = lazy.addLazyItem(elementRef)

    // Удаление из отслеживания при скрытии
    watch(showElement, (show) => {
      if (!show && elementRef.value) {
        lazy.removeLazyItem(elementRef.value)
      }
    })

    return {
      elementRef,
      showElement,
      isVisible
    }
  }
}
```

### Предзагрузка с rootMargin

```javascript
// По умолчанию rootMargin: '128px 0px'
// Элементы начинают отслеживаться за 128px до входа в viewport

const lazy = useLazyRef()
const imageRef = ref(null)

// Изображение начнёт загружаться за 128px до видимости
const isVisible = lazy.addLazyItem(imageRef)

console.log('rootMargin обеспечивает плавную предзагрузку')
```

## Обработка отсутствия IntersectionObserver

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

const lazy = useLazyRef()

// Проверка доступности API
if (lazy.intersectionObserver) {
  console.log('IntersectionObserver доступен')
  // Используем ленивую загрузку
} else {
  console.log('IntersectionObserver не поддерживается')
  // isVisible всегда будет true (fallback)
}

const elementRef = ref(null)
const isVisible = lazy.addLazyItem(elementRef)

// В старых браузерах isVisible.value === true сразу
// Контент загружается немедленно (graceful degradation)
```

## Оптимизация производительности

### Ленивая загрузка в списках

```javascript
import { ref } from 'vue'
import { useLazyRef } from '@dxtmisha/functional'

export default {
  setup() {
    const lazy = useLazyRef()
    const products = ref([
      { id: 1, name: 'Товар 1', image: '/img1.jpg' },
      { id: 2, name: 'Товар 2', image: '/img2.jpg' },
      // ... ещё 100 товаров
    ])

    // Создание refs для каждого товара
    const productRefs = ref(products.value.map(() => ({
      element: ref(null),
      visible: null
    })))

    // Отслеживание видимости каждого товара
    productRefs.value.forEach(item => {
      item.visible = lazy.addLazyItem(item.element)
    })

    return {
      products,
      productRefs
    }
  }
}

// Template:
// <div v-for="(product, index) in products" :key="product.id">
//   <div :ref="productRefs[index].element">
//     <template v-if="productRefs[index].visible.value">
//       <img :src="product.image" :alt="product.name" />
//       <h3>{{ product.name }}</h3>
//     </template>
//     <div v-else class="product-skeleton">Загрузка...</div>
//   </div>
// </div>
```

