{"version":3,"file":"use-scroll-spy.cjs","names":["randomId"],"sources":["../../src/use-scroll-spy/use-scroll-spy.ts"],"sourcesContent":["import { useEffect, 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 = () => {\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]);\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,EAAE;AAE5C,MAAK,IAAI,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK,GAAG;EAC3C,MAAM,UAAU,SAAS;AACzB,SAAO,KAAK;GACV,OAAO,SAAS,QAAQ;GACxB,OAAO,SAAS,QAAQ;GACxB,IAAI,QAAQ,MAAMA,kBAAAA,UAAU;GAC5B,eAAgB,QAAQ,KAAK,SAAS,eAAe,QAAQ,GAAG,GAAI;GACrE,CAAC;;AAGJ,QAAO;;AAGT,SAAS,iBAAiB,OAAkB,SAAiB,GAAG;AAC9D,KAAI,MAAM,WAAW,EACnB,QAAO;AAiBT,QAdgB,MAAM,QACnB,KAAK,MAAM,UAAU;AACpB,MAAI,KAAK,IAAI,IAAI,WAAW,OAAO,GAAG,KAAK,IAAI,KAAK,IAAI,OAAO,CAC7D,QAAO;AAGT,SAAO;GACL;GACA,UAAU,KAAK;GAChB;IAEH;EAAE,OAAO;EAAG,UAAU,MAAM,GAAG;EAAG,CACnC,CAEc;;AAGjB,SAAS,gBAAgB,SAAsB;AAC7C,QAAO,OAAO,QAAQ,QAAQ,GAAG;;AAGnC,SAAS,gBAAgB,SAAsB;AAC7C,QAAO,QAAQ,eAAe;;AAgDhC,SAAgB,aAAa,EAC3B,WAAW,0BACX,WAAW,iBACX,WAAW,iBACX,SAAS,GACT,eACuB,EAAE,EAA2B;CACpD,MAAM,CAAC,QAAQ,cAAA,GAAA,MAAA,UAAsB,GAAG;CACxC,MAAM,CAAC,aAAa,mBAAA,GAAA,MAAA,UAA2B,MAAM;CACrD,MAAM,CAAC,MAAM,YAAA,GAAA,MAAA,UAA+C,EAAE,CAAC;CAC/D,MAAM,eAAA,GAAA,MAAA,QAAgD,EAAE,CAAC;CAEzD,MAAM,qBAAqB;AACzB,YACE,iBACE,YAAY,QAAQ,KAAK,MAAM,EAAE,SAAS,CAAC,uBAAuB,CAAC,EACnE,OACD,CACF;;CAGH,MAAM,mBAAmB;EACvB,MAAM,WAAW,gBACf,MAAM,KAAK,SAAS,iBAAiB,SAAS,CAAC,EAC/C,UACA,SACD;AACD,cAAY,UAAU;AACtB,iBAAe,KAAK;AACpB,UAAQ,SAAS;AACjB,YACE,iBACE,SAAS,KAAK,MAAM,EAAE,SAAS,CAAC,uBAAuB,CAAC,EACxD,OACD,CACF;;AAGH,EAAA,GAAA,MAAA,iBAAgB;AACd,cAAY;EACZ,MAAM,cAAc,cAAc;AAClC,cAAY,iBAAiB,UAAU,aAAa;AACpD,eAAa,YAAY,oBAAoB,UAAU,aAAa;IACnE,CAAC,WAAW,CAAC;AAEhB,QAAO;EACL,cAAc;EACd;EACA;EACA;EACD"}