{"version":3,"file":"index.mjs","names":["gap"],"sources":["../src/index.ts"],"sourcesContent":["import { debounce } from '@yeger/debounce'\nimport type { LifecycleHook, VueRef, VueVersion, Watch } from '@yeger/vue-lib-adapter'\n\nexport type NonEmptyArray<T> = [T, ...T[]]\n\nexport type Column = number[]\n\nexport interface Vue2ComponentEmits {\n  (event: 'redraw'): void\n  (event: 'redraw-skip'): void\n}\n\nexport interface Vue3ComponentEmits {\n  (event: 'redraw'): void\n  (event: 'redrawSkip'): void\n}\n\nexport interface HookProps<T> {\n  columns: VueRef<Column[]>\n  columnWidth: VueRef<number | NonEmptyArray<number>>\n  emit: Vue2ComponentEmits | Vue3ComponentEmits\n  gap: VueRef<number>\n  items: VueRef<T[]>\n  maxColumns: VueRef<number | undefined>\n  minColumns: VueRef<number | undefined>\n  nextTick: () => Promise<void>\n  onBeforeUnmount: LifecycleHook\n  onMounted: LifecycleHook\n  rtl: VueRef<boolean>\n  scrollContainer: VueRef<HTMLElement | null>\n  ssrColumns: VueRef<number>\n  vue: VueVersion\n  wall: VueRef<HTMLDivElement>\n  watch: Watch\n}\n\nexport function useMasonryWall<T>({\n  columns,\n  columnWidth,\n  emit,\n  gap,\n  items,\n  maxColumns,\n  minColumns,\n  nextTick,\n  onBeforeUnmount,\n  onMounted,\n  rtl,\n  scrollContainer,\n  ssrColumns,\n  vue,\n  wall,\n  watch,\n}: HookProps<T>): { getColumnWidthTarget: (columnIndex: number) => number } {\n  function countIteratively(\n    containerWidth: number,\n    gap: number,\n    count: number,\n    consumed: number,\n  ): number {\n    const nextWidth = getColumnWidthTarget(count)\n    if (consumed + gap + nextWidth <= containerWidth) {\n      return countIteratively(containerWidth, gap, count + 1, consumed + gap + nextWidth)\n    }\n    return count\n  }\n\n  function getColumnWidthTarget(columnIndex: number): number {\n    const widths = Array.isArray(columnWidth.value) ? columnWidth.value : [columnWidth.value]\n    return widths[columnIndex % widths.length] as number\n  }\n\n  function columnCount(): number {\n    const count = countIteratively(\n      wall.value.getBoundingClientRect().width,\n      gap.value,\n      0,\n      // Needs to be offset my negative gap to prevent gap counts being off by one\n      -gap.value,\n    )\n    const boundedCount = aboveMin(belowMax(count))\n    return boundedCount > 0 ? boundedCount : 1\n  }\n\n  function belowMax(count: number): number {\n    const max = maxColumns?.value\n    if (!max) {\n      return count\n    }\n    return count > max ? max : count\n  }\n\n  function aboveMin(count: number): number {\n    const min = minColumns?.value\n    if (!min) {\n      return count\n    }\n    return count < min ? min : count\n  }\n\n  if (ssrColumns.value > 0) {\n    const newColumns = createColumns(ssrColumns.value)\n    items.value.forEach((_: T, i: number) => newColumns[i % ssrColumns.value]!.push(i))\n    columns.value = newColumns\n  }\n\n  let currentRedrawId = 0\n\n  async function fillColumns(itemIndex: number, assignedRedrawId: number) {\n    if (itemIndex >= items.value.length) {\n      return\n    }\n    await nextTick()\n    if (currentRedrawId !== assignedRedrawId) {\n      // Skip if a new redraw has been requested in parallel,\n      // e.g., in an onMounted hook during initial render\n      return\n    }\n    const columnDivs = [...wall.value.children] as HTMLDivElement[]\n    if (rtl.value) {\n      columnDivs.reverse()\n    }\n    const target = columnDivs.reduce((prev, curr) =>\n      curr.getBoundingClientRect().height < prev.getBoundingClientRect().height ? curr : prev,\n    )\n    columns.value[+target.dataset.index!]!.push(itemIndex)\n    await fillColumns(itemIndex + 1, assignedRedrawId)\n  }\n\n  async function redraw(force = false) {\n    const newColumnCount = columnCount()\n    if (columns.value.length === newColumnCount && !force) {\n      if (vue === 2) {\n        ;(emit as Vue2ComponentEmits)('redraw-skip')\n      } else {\n        ;(emit as Vue3ComponentEmits)('redrawSkip')\n      }\n      return\n    }\n    columns.value = createColumns(newColumnCount)\n    const scrollTarget = scrollContainer?.value\n    const scrollY = scrollTarget ? scrollTarget.scrollTop : window.scrollY\n    await fillColumns(0, ++currentRedrawId)\n    if (scrollTarget) {\n      scrollTarget.scrollBy({ top: scrollY - scrollTarget.scrollTop })\n    } else {\n      window.scrollTo({ top: scrollY })\n    }\n    emit('redraw')\n  }\n\n  const resizeObserver =\n    typeof ResizeObserver === 'undefined' ? undefined : new ResizeObserver(debounce(() => redraw()))\n\n  onMounted(async () => {\n    await redraw()\n    resizeObserver?.observe(wall.value)\n  })\n\n  onBeforeUnmount(() => resizeObserver?.unobserve(wall.value))\n\n  watch([items, rtl], () => redraw(true))\n  watch([columnWidth, gap, minColumns, maxColumns], () => redraw())\n\n  return { getColumnWidthTarget }\n}\n\nfunction createColumns(count: number): Column[] {\n  return Array.from({ length: count }).map(() => [])\n}\n"],"mappings":";;;AAoCA,SAAgB,eAAkB,EAChC,SACA,aACA,MACA,KACA,OACA,YACA,YACA,UACA,iBACA,WACA,KACA,iBACA,YACA,KACA,MACA,SAC0E;CAC1E,SAAS,iBACP,gBACA,OACA,OACA,UACQ;EACR,MAAM,YAAY,qBAAqB,MAAM;AAC7C,MAAI,WAAWA,QAAM,aAAa,eAChC,QAAO,iBAAiB,gBAAgBA,OAAK,QAAQ,GAAG,WAAWA,QAAM,UAAU;AAErF,SAAO;;CAGT,SAAS,qBAAqB,aAA6B;EACzD,MAAM,SAAS,MAAM,QAAQ,YAAY,MAAM,GAAG,YAAY,QAAQ,CAAC,YAAY,MAAM;AACzF,SAAO,OAAO,cAAc,OAAO;;CAGrC,SAAS,cAAsB;EAQ7B,MAAM,eAAe,SAAS,SAPhB,iBACZ,KAAK,MAAM,uBAAuB,CAAC,OACnC,IAAI,OACJ,GAEA,CAAC,IAAI,MACN,CAC4C,CAAC;AAC9C,SAAO,eAAe,IAAI,eAAe;;CAG3C,SAAS,SAAS,OAAuB;EACvC,MAAM,MAAM,YAAY;AACxB,MAAI,CAAC,IACH,QAAO;AAET,SAAO,QAAQ,MAAM,MAAM;;CAG7B,SAAS,SAAS,OAAuB;EACvC,MAAM,MAAM,YAAY;AACxB,MAAI,CAAC,IACH,QAAO;AAET,SAAO,QAAQ,MAAM,MAAM;;AAG7B,KAAI,WAAW,QAAQ,GAAG;EACxB,MAAM,aAAa,cAAc,WAAW,MAAM;AAClD,QAAM,MAAM,SAAS,GAAM,MAAc,WAAW,IAAI,WAAW,OAAQ,KAAK,EAAE,CAAC;AACnF,UAAQ,QAAQ;;CAGlB,IAAI,kBAAkB;CAEtB,eAAe,YAAY,WAAmB,kBAA0B;AACtE,MAAI,aAAa,MAAM,MAAM,OAC3B;AAEF,QAAM,UAAU;AAChB,MAAI,oBAAoB,iBAGtB;EAEF,MAAM,aAAa,CAAC,GAAG,KAAK,MAAM,SAAS;AAC3C,MAAI,IAAI,MACN,YAAW,SAAS;EAEtB,MAAM,SAAS,WAAW,QAAQ,MAAM,SACtC,KAAK,uBAAuB,CAAC,SAAS,KAAK,uBAAuB,CAAC,SAAS,OAAO,KACpF;AACD,UAAQ,MAAM,CAAC,OAAO,QAAQ,OAAS,KAAK,UAAU;AACtD,QAAM,YAAY,YAAY,GAAG,iBAAiB;;CAGpD,eAAe,OAAO,QAAQ,OAAO;EACnC,MAAM,iBAAiB,aAAa;AACpC,MAAI,QAAQ,MAAM,WAAW,kBAAkB,CAAC,OAAO;AACrD,OAAI,QAAQ,EACT,CAAC,KAA4B,cAAc;OAE3C,CAAC,KAA4B,aAAa;AAE7C;;AAEF,UAAQ,QAAQ,cAAc,eAAe;EAC7C,MAAM,eAAe,iBAAiB;EACtC,MAAM,UAAU,eAAe,aAAa,YAAY,OAAO;AAC/D,QAAM,YAAY,GAAG,EAAE,gBAAgB;AACvC,MAAI,aACF,cAAa,SAAS,EAAE,KAAK,UAAU,aAAa,WAAW,CAAC;MAEhE,QAAO,SAAS,EAAE,KAAK,SAAS,CAAC;AAEnC,OAAK,SAAS;;CAGhB,MAAM,iBACJ,OAAO,mBAAmB,cAAc,SAAY,IAAI,eAAe,eAAe,QAAQ,CAAC,CAAC;AAElG,WAAU,YAAY;AACpB,QAAM,QAAQ;AACd,kBAAgB,QAAQ,KAAK,MAAM;GACnC;AAEF,uBAAsB,gBAAgB,UAAU,KAAK,MAAM,CAAC;AAE5D,OAAM,CAAC,OAAO,IAAI,QAAQ,OAAO,KAAK,CAAC;AACvC,OAAM;EAAC;EAAa;EAAK;EAAY;EAAW,QAAQ,QAAQ,CAAC;AAEjE,QAAO,EAAE,sBAAsB;;AAGjC,SAAS,cAAc,OAAyB;AAC9C,QAAO,MAAM,KAAK,EAAE,QAAQ,OAAO,CAAC,CAAC,UAAU,EAAE,CAAC"}