{"version":3,"file":"pagination.mjs","names":[],"sources":["../../../../package/common/pagination/pagination.tsx"],"sourcesContent":["import { useState } from 'react';\nimport type { ReactElement } from 'react';\nimport cx from 'classnames';\nimport { faAnglesLeft, faAnglesRight, faChevronLeft, faChevronRight } from '@fortawesome/free-solid-svg-icons';\n\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport type { IconContent } from '../types/icon.types';\nimport { renderIcon } from '../utils/renderIcon';\nimport type { PaginationLabels, PaginationProps } from './pagination.types';\n\nimport styles from './pagination.module.pcss';\n\nconst DOTS = 'dots';\n\ntype PaginationEntry = number | typeof DOTS;\n\nconst DEFAULT_LABELS: Required<PaginationLabels> = {\n  goToPage: (page) => `Go to page ${page}`,\n  first: 'Go to first page',\n  previous: 'Go to previous page',\n  next: 'Go to next page',\n  last: 'Go to last page',\n};\n\nfunction range(start: number, end: number): number[] {\n  if (end < start) return [];\n  return Array.from({ length: end - start + 1 }, (_, index) => start + index);\n}\n\nfunction clampPage(page: number, total: number): number {\n  return Math.max(1, Math.min(total, Math.round(page)));\n}\n\nfunction getEntries(page: number, total: number, siblings: number, boundaries: number): PaginationEntry[] {\n  const maxEntries = siblings * 2 + boundaries * 2 + 3;\n  if (maxEntries >= total) return range(1, total);\n\n  const leftBoundary = range(1, boundaries);\n  const rightBoundary = range(total - boundaries + 1, total);\n  const leftGap: PaginationEntry[] = leftBoundary.length > 0 ? [DOTS] : [];\n  const rightGap: PaginationEntry[] = rightBoundary.length > 0 ? [DOTS] : [];\n\n  const leftSibling = Math.max(page - siblings, boundaries + 1);\n  const rightSibling = Math.min(page + siblings, total - boundaries);\n\n  const hasLeftDots = leftSibling > boundaries + 2;\n  const hasRightDots = rightSibling < total - boundaries - 1;\n\n  if (!hasLeftDots && hasRightDots) {\n    return [...range(1, boundaries + siblings * 2 + 2), ...rightGap, ...rightBoundary];\n  }\n\n  if (hasLeftDots && !hasRightDots) {\n    return [...leftBoundary, ...leftGap, ...range(total - boundaries - siblings * 2 - 1, total)];\n  }\n\n  return [...leftBoundary, ...leftGap, ...range(leftSibling, rightSibling), ...rightGap, ...rightBoundary];\n}\n\nfunction Pagination(props: PaginationProps): ReactElement | null {\n  const {\n    total,\n    page: controlledPage,\n    defaultPage = 1,\n    onChange = () => {},\n    siblings = 1,\n    boundaries = 1,\n    showControls = true,\n    showEdges = false,\n    disabled = false,\n    previousIcon = faChevronLeft,\n    nextIcon = faChevronRight,\n    firstIcon = faAnglesLeft,\n    lastIcon = faAnglesRight,\n    'aria-label': ariaLabel = 'Pagination',\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    labels,\n    ...rest\n  } = useComponentDefaults('Pagination', props);\n  const text = useLabels('Pagination', labels, DEFAULT_LABELS);\n\n  const isControlled = controlledPage !== undefined;\n  const [internalPage, setInternalPage] = useState(defaultPage);\n  const page = clampPage(isControlled ? controlledPage! : internalPage, total);\n\n  if (total < 1) return null;\n\n  function goTo(next: number) {\n    const clamped = clampPage(next, total);\n    if (clamped === page) return;\n    if (!isControlled) setInternalPage(clamped);\n    onChange(clamped);\n  }\n\n  const controlClassName = cx(styles.paginationControl, classNames?.control);\n\n  function renderControl(key: string, label: string, icon: IconContent, target: number, isDisabled: boolean) {\n    return (\n      <button\n        key={key}\n        type='button'\n        className={controlClassName}\n        style={slotStyles?.control}\n        aria-label={label}\n        disabled={disabled || isDisabled}\n        onClick={() => goTo(target)}\n      >\n        {renderIcon(icon)}\n      </button>\n    );\n  }\n\n  const isFirst = page <= 1;\n  const isLast = page >= total;\n\n  return (\n    <nav\n      {...rest}\n      aria-label={ariaLabel}\n      className={cx(styles.pagination, classNames?.root, className)}\n      style={{ ...style, ...slotStyles?.root }}\n    >\n      {showEdges && renderControl('first', text.first, firstIcon, 1, isFirst)}\n      {showControls && renderControl('previous', text.previous, previousIcon, page - 1, isFirst)}\n\n      {getEntries(page, total, siblings, boundaries).map((entry, index) =>\n        entry === DOTS ? (\n          <span\n            key={`dots-${index}`}\n            aria-hidden='true'\n            className={cx(styles.paginationEllipsis, classNames?.ellipsis)}\n            style={slotStyles?.ellipsis}\n          >\n            &hellip;\n          </span>\n        ) : (\n          <button\n            key={entry}\n            type='button'\n            className={cx(styles.paginationItem, entry === page && styles.paginationItemActive, classNames?.item)}\n            style={slotStyles?.item}\n            aria-label={text.goToPage(entry)}\n            aria-current={entry === page ? 'page' : undefined}\n            disabled={disabled}\n            onClick={() => goTo(entry)}\n          >\n            {entry}\n          </button>\n        ),\n      )}\n\n      {showControls && renderControl('next', text.next, nextIcon, page + 1, isLast)}\n      {showEdges && renderControl('last', text.last, lastIcon, total, isLast)}\n    </nav>\n  );\n}\n\nexport default Pagination;\n"],"mappings":"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"}