Перейти к содержимому
GitHubXDiscord

@triggery/dom

DOM-мосты для Triggery — направляй обратные вызовы addEventListener, ResizeObserver и IntersectionObserver в события триггеров. Все три хука цепляются в useEffect (commit-фаза, безопасны для StrictMode) и отцепляются при unmount.

npm bundle

pnpm add @triggery/core @triggery/react @triggery/dom

Peer-зависимости: react >= 18.0.0.

ЭкспортНазначение
useDomEvent(trigger, eventName, target, domEventName, options?)Пробрасывает DOM-событие из элемента, window/document или React-ref.
useResizeObserver(trigger, eventName, ref, options?)Пробрасывает записи ResizeObserver по ref’у элемента.
useIntersectionObserver(trigger, eventName, ref, options?)Пробрасывает записи IntersectionObserver по ref’у элемента.

target может быть EventTarget (например, window), React-ref или null / undefined (no-op, пока не передан реальный target).

import { useRef } from 'react';
import {
  useDomEvent,
  useResizeObserver,
  useIntersectionObserver,
} from '@triggery/dom';

function Input() {
  const ref = useRef<HTMLInputElement>(null);
  useDomEvent(chatTrigger, 'submit', ref, 'keydown', {
    mapPayload: (e) => ({ key: (e as KeyboardEvent).key }),
    listenerOptions: { passive: true },
  });
  return <input ref={ref} />;
}

function GlobalEscapeWatcher() {
  useDomEvent(uiTrigger, 'escape', window, 'keydown', {
    mapPayload: (e) => (e as KeyboardEvent).key,
  });
  return null;
}

function Panel() {
  const ref = useRef<HTMLDivElement>(null);
  useResizeObserver(layoutTrigger, 'panel-resized', ref, {
    mapPayload: (e) => ({
      width:  e.contentRect.width,
      height: e.contentRect.height,
    }),
  });
  return <div ref={ref}>...</div>;
}

function VirtualRow() {
  const ref = useRef<HTMLLIElement>(null);
  useIntersectionObserver(virtualTrigger, 'row-visible', ref, {
    rootMargin: '200px',
    mapPayload: (e) => ({ visible: e.isIntersecting, ratio: e.intersectionRatio }),
  });
  return <li ref={ref}>...</li>;
}

Идентичность слушателя стабильна между рендерами, пока mapPayload / listenerOptions / target фактически не изменились, — поэтому React не пересоздаёт подписку на каждом рендере. Тип события в триггере определяет, что должен возвращать mapPayload. Если mapPayload опущен, исходное DOM-событие или запись observer’а пробрасывается как есть.