{"version":3,"file":"use-scroll-spy.cjs","names":["randomId"],"sources":["../../src/use-scroll-spy/use-scroll-spy.ts"],"sourcesContent":["import { useEffect, useEffectEvent, useRef, useState } from 'react';\nimport { randomId } from '../utils';\n\nfunction getHeadingsData(\n  headings: HTMLElement[],\n  getDepth: (element: HTMLElement) => number,\n  getValue: (element: HTMLElement) => string\n): UseScrollSpyHeadingData[] {\n  const result: UseScrollSpyHeadingData[] = [];\n\n  for (let i = 0; i < headings.length; i += 1) {\n    const heading = headings[i];\n    result.push({\n      depth: getDepth(heading),\n      value: getValue(heading),\n      id: heading.id || randomId(),\n      getNode: () => (heading.id ? document.getElementById(heading.id)! : heading),\n    });\n  }\n\n  return result;\n}\n\nfunction getActiveElement(rects: DOMRect[], offset: number = 0) {\n  if (rects.length === 0) {\n    return -1;\n  }\n\n  const closest = rects.reduce(\n    (acc, item, index) => {\n      if (Math.abs(acc.position - offset) < Math.abs(item.y - offset)) {\n        return acc;\n      }\n\n      return {\n        index,\n        position: item.y,\n      };\n    },\n    { index: 0, position: rects[0].y }\n  );\n\n  return closest.index;\n}\n\nfunction getDefaultDepth(element: HTMLElement) {\n  return Number(element.tagName[1]);\n}\n\nfunction getDefaultValue(element: HTMLElement) {\n  return element.textContent || '';\n}\n\nexport interface UseScrollSpyHeadingData {\n  /** Heading depth, 1-6 */\n  depth: number;\n\n  /** Heading text content value */\n  value: string;\n\n  /** Heading id */\n  id: string;\n\n  /** Function to get heading node */\n  getNode: () => HTMLElement;\n}\n\nexport interface UseScrollSpyOptions {\n  /** Selector to get headings, `'h1, h2, h3, h4, h5, h6'` by default */\n  selector?: string;\n\n  /** A function to retrieve depth of heading, by default depth is calculated based on tag name */\n  getDepth?: (element: HTMLElement) => number;\n\n  /** A function to retrieve heading value, by default `element.textContent` is used */\n  getValue?: (element: HTMLElement) => string;\n\n  /** Host element to attach scroll event listener, if not provided, `window` is used */\n  scrollHost?: HTMLElement;\n\n  /** Offset from the top of the viewport to use when determining the active heading, `0` by default */\n  offset?: number;\n}\n\nexport interface UseScrollSpyReturnValue {\n  /** Index of the active heading in the `data` array */\n  active: number;\n\n  /** Headings data. If not initialize, data is represented by an empty array. */\n  data: UseScrollSpyHeadingData[];\n\n  /** True if headings value have been retrieved from the DOM. */\n  initialized: boolean;\n\n  /** Function to update headings values after the parent component has mounted. */\n  reinitialize: () => void;\n}\n\nexport function useScrollSpy({\n  selector = 'h1, h2, h3, h4, h5, h6',\n  getDepth = getDefaultDepth,\n  getValue = getDefaultValue,\n  offset = 0,\n  scrollHost,\n}: UseScrollSpyOptions = {}): UseScrollSpyReturnValue {\n  const [active, setActive] = useState(-1);\n  const [initialized, setInitialized] = useState(false);\n  const [data, setData] = useState<UseScrollSpyHeadingData[]>([]);\n  const headingsRef = useRef<UseScrollSpyHeadingData[]>([]);\n\n  const handleScroll = useEffectEvent(() => {\n    setActive(\n      getActiveElement(\n        headingsRef.current.map((d) => d.getNode().getBoundingClientRect()),\n        offset\n      )\n    );\n  });\n\n  const initialize = () => {\n    const headings = getHeadingsData(\n      Array.from(document.querySelectorAll(selector)),\n      getDepth,\n      getValue\n    );\n    headingsRef.current = headings;\n    setInitialized(true);\n    setData(headings);\n    setActive(\n      getActiveElement(\n        headings.map((d) => d.getNode().getBoundingClientRect()),\n        offset\n      )\n    );\n  };\n\n  useEffect(() => {\n    initialize();\n    const _scrollHost = scrollHost || window;\n    _scrollHost.addEventListener('scroll', handleScroll);\n    return () => _scrollHost.removeEventListener('scroll', handleScroll);\n  }, [scrollHost, selector, offset]);\n\n  return {\n    reinitialize: initialize,\n    active,\n    initialized,\n    data,\n  };\n}\n\nexport namespace useScrollSpy {\n  export type Options = UseScrollSpyOptions;\n  export type ReturnValue = UseScrollSpyReturnValue;\n}\n"],"mappings":";;;;AAGA,SAAS,gBACP,UACA,UACA,UAC2B;CAC3B,MAAM,SAAoC,CAAC;CAE3C,KAAK,IAAI,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK,GAAG;EAC3C,MAAM,UAAU,SAAS;EACzB,OAAO,KAAK;GACV,OAAO,SAAS,OAAO;GACvB,OAAO,SAAS,OAAO;GACvB,IAAI,QAAQ,MAAMA,kBAAAA,SAAS;GAC3B,eAAgB,QAAQ,KAAK,SAAS,eAAe,QAAQ,EAAE,IAAK;EACtE,CAAC;CACH;CAEA,OAAO;AACT;AAEA,SAAS,iBAAiB,OAAkB,SAAiB,GAAG;CAC9D,IAAI,MAAM,WAAW,GACnB,OAAO;CAiBT,OAdgB,MAAM,QACnB,KAAK,MAAM,UAAU;EACpB,IAAI,KAAK,IAAI,IAAI,WAAW,MAAM,IAAI,KAAK,IAAI,KAAK,IAAI,MAAM,GAC5D,OAAO;EAGT,OAAO;GACL;GACA,UAAU,KAAK;EACjB;CACF,GACA;EAAE,OAAO;EAAG,UAAU,MAAM,GAAG;CAAE,CAGtB,EAAE;AACjB;AAEA,SAAS,gBAAgB,SAAsB;CAC7C,OAAO,OAAO,QAAQ,QAAQ,EAAE;AAClC;AAEA,SAAS,gBAAgB,SAAsB;CAC7C,OAAO,QAAQ,eAAe;AAChC;AA+CA,SAAgB,aAAa,EAC3B,WAAW,0BACX,WAAW,iBACX,WAAW,iBACX,SAAS,GACT,eACuB,CAAC,GAA4B;CACpD,MAAM,CAAC,QAAQ,cAAA,GAAA,MAAA,UAAsB,EAAE;CACvC,MAAM,CAAC,aAAa,mBAAA,GAAA,MAAA,UAA2B,KAAK;CACpD,MAAM,CAAC,MAAM,YAAA,GAAA,MAAA,UAA+C,CAAC,CAAC;CAC9D,MAAM,eAAA,GAAA,MAAA,QAAgD,CAAC,CAAC;CAExD,MAAM,gBAAA,GAAA,MAAA,sBAAoC;EACxC,UACE,iBACE,YAAY,QAAQ,KAAK,MAAM,EAAE,QAAQ,EAAE,sBAAsB,CAAC,GAClE,MACF,CACF;CACF,CAAC;CAED,MAAM,mBAAmB;EACvB,MAAM,WAAW,gBACf,MAAM,KAAK,SAAS,iBAAiB,QAAQ,CAAC,GAC9C,UACA,QACF;EACA,YAAY,UAAU;EACtB,eAAe,IAAI;EACnB,QAAQ,QAAQ;EAChB,UACE,iBACE,SAAS,KAAK,MAAM,EAAE,QAAQ,EAAE,sBAAsB,CAAC,GACvD,MACF,CACF;CACF;CAEA,CAAA,GAAA,MAAA,iBAAgB;EACd,WAAW;EACX,MAAM,cAAc,cAAc;EAClC,YAAY,iBAAiB,UAAU,YAAY;EACnD,aAAa,YAAY,oBAAoB,UAAU,YAAY;CACrE,GAAG;EAAC;EAAY;EAAU;CAAM,CAAC;CAEjC,OAAO;EACL,cAAc;EACd;EACA;EACA;CACF;AACF"}